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 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 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 69 RequestContext* RequestContext::current() { 70 DCHECK(g_current_context.Pointer()->Get()); 71 return g_current_context.Pointer()->Get(); 72 } 73 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 83 void RequestContext::AddWatchCancelFinalizer(scoped_refptr<Watcher> watcher) { 84 DCHECK(IsCurrent()); 85 watch_cancel_finalizers_->push_back(watcher); 86 } 87 88 bool RequestContext::IsCurrent() const { 89 return tls_context_->Get() == this; 90 } 91 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 102 RequestContext::WatchNotifyFinalizer::~WatchNotifyFinalizer() {} 103 104 } // namespace edk 105 } // namespace mojo 106