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/edk/system/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 edk {
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 MojoWatchNotificationFlags flags = MOJO_WATCH_NOTIFICATION_FLAG_NONE;
40 if (source_ == Source::SYSTEM)
41 flags |= MOJO_WATCH_NOTIFICATION_FLAG_FROM_SYSTEM;
42
43 // We run all cancellation finalizers first. This is necessary because it's
44 // possible that one of the cancelled watchers has other pending finalizers
45 // attached to this RequestContext.
46 //
47 // From the application's perspective the watch has already been cancelled,
48 // so we have to honor our contract which guarantees no more notifications.
49 for (const scoped_refptr<Watcher>& watcher :
50 watch_cancel_finalizers_.container())
51 watcher->Cancel();
52
53 for (const WatchNotifyFinalizer& watch :
54 watch_notify_finalizers_.container()) {
55 // Establish a new request context for the extent of each callback to
56 // ensure that they don't themselves invoke callbacks while holding a
57 // watcher lock.
58 RequestContext request_context(source_);
59 watch.watcher->MaybeInvokeCallback(watch.result, watch.state, flags);
60 }
61 } else {
62 // It should be impossible for nested contexts to have finalizers.
63 DCHECK(watch_notify_finalizers_.container().empty());
64 DCHECK(watch_cancel_finalizers_.container().empty());
65 }
66 }
67
68 // static
current()69 RequestContext* RequestContext::current() {
70 DCHECK(g_current_context.Pointer()->Get());
71 return g_current_context.Pointer()->Get();
72 }
73
AddWatchNotifyFinalizer(scoped_refptr<Watcher> watcher,MojoResult result,const HandleSignalsState & state)74 void RequestContext::AddWatchNotifyFinalizer(
75 scoped_refptr<Watcher> watcher,
76 MojoResult result,
77 const HandleSignalsState& state) {
78 DCHECK(IsCurrent());
79 watch_notify_finalizers_->push_back(
80 WatchNotifyFinalizer(watcher, result, state));
81 }
82
AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher)83 void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) {
84 DCHECK(IsCurrent());
85 watch_cancel_finalizers_->push_back(watcher);
86 }
87
IsCurrent() const88 bool RequestContext::IsCurrent() const {
89 return tls_context_->Get() == this;
90 }
91
WatchNotifyFinalizer(scoped_refptr<Watcher> watcher,MojoResult result,const HandleSignalsState & state)92 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
93 scoped_refptr<Watcher> watcher,
94 MojoResult result,
95 const HandleSignalsState& state)
96 : watcher(watcher), result(result), state(state) {
97 }
98
99 RequestContext::WatchNotifyFinalizer::WatchNotifyFinalizer(
100 const WatchNotifyFinalizer& other) = default;
101
~WatchNotifyFinalizer()102 RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {}
103
104 } // namespace edk
105 } // namespace mojo
106