• 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/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