1 // Copyright 2017 The Fuchsia 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 #pragma once
6 
7 #include <zircon/assert.h>
8 #include <zircon/compiler.h>
9 #include <zircon/types.h>
10 #include <lib/zx/event.h>
11 #include <fbl/intrusive_double_list.h>
12 #include <fbl/mutex.h>
13 #include <fbl/ref_counted.h>
14 #include <fbl/ref_ptr.h>
15 
16 #include <dispatcher-pool/dispatcher-event-source.h>
17 
18 #include <atomic>
19 
20 namespace dispatcher {
21 
22 class ThreadPool;
23 
24 // class ExecutionDomain
25 //
26 // In the dispatcher framework, ExecutionDomains represent a context which
27 // specific types of EventSources become bound to during activation.  While many
28 // EventSources may have interesting things happening on them simultaniously,
29 // the ExecutionDomain they are bound to guarantees that only one EventSource's
30 // handler will be executed at any given point in time.
31 //
32 // Once created using the static Create method, an ExecutionDomain is
33 // immediately Active.  New EventSources may be activated on it until the domain
34 // becomes deactivated, at which point new attempts to activate an event source
35 // on the execution domain will fail.
36 //
37 // Deactivating an execution domain will automatically deactivate all event
38 // sources currently bound to the domain.  When deactivated from outside of the
39 // context of a dispatch operation, callers use the Deactivate method and will
40 // be synchronized with any currently in-flight dispatch operations.  IOW -
41 // after Deactivate() returns, all dispatch operatons are guaranteed to be
42 // finished and no new dispatch operations will be started.
43 //
44 // If an execution domain needs to be deactivated from the context of a dispatch
45 // operations, the DeactivateFromWithinDomain method is used instead.  No new
46 // dispatch operations will be started, and the system will be completely
47 // deactivated when the current in-flight dispatch operation unwinds.
48 //
49 class ExecutionDomain : public fbl::RefCounted<ExecutionDomain> {
50 public:
51     // Token and ScopedToken are small (empty) objects which are intended to be
52     // used with the clang static thread analysis framework in order to express
53     // concurrency guarantees which arise from the serialized-dispatch behavior
54     // provided by ExecutionsDomains.  By obtaining the capability represented
55     // by an execution domain's token in an event source's processing handler,
56     // users may assert at compile time that they are executing in handlers in a
57     // serialized fashion imposed by a particular execution domain.  For
58     // example...
59     //
60     // class Thingy {
61     //   ...
62     //   // Require that we be running in my_domain_ in order to call handle
63     //   // channel.
64     //   void HandleChannel(Channel* ch) __TA_REQUIRES(my_domain_.token());
65     //   ...
66     //
67     //   // This variable can only be changes while running in my_domain_
68     //   uint32_t my_state_ __TA_GUARDED(my_domain_.token());
69     // }
70     //
71     // void Thingy::Activate() {
72     //     Channel::ProcessHandler phandler(
73     //     [thingy = fbl::WrapRefPtr(this)](Channel* ch) {
74     //       // Establish the fact that this callback is running in my_domain_
75     //       ExecutionDomain::ScopedToken token(my_domain_->token());
76     //       thingy->HandleChannel(ch);
77     //     });
78     //
79     //     my_channel_.Activate(..., std::move(phandler), ...);
80     //
81     //     my_state_++;  // This fails, we are not running in the domain.
82     // }
83     //
84     // void Thingy::HandleChannel(Channel* ch) {
85     //     my_state_++;  // This succeeds, we are running in the domain.
86     //     DeactivateFromWithinDomain(); // So does this.
87     //
88     //     // This fails, we should call not Deactivate from within the domain.
89     //     Deactivate();
90     // }
91     //
92     //
93     struct __TA_CAPABILITY("role") Token { };
94     class __TA_SCOPED_CAPABILITY ScopedToken {
95     public:
ScopedToken(const Token & token)96         explicit ScopedToken(const Token& token) __TA_ACQUIRE(token) { }
__TA_RELEASE()97         ~ScopedToken() __TA_RELEASE() { }
98     };
99 
100     static constexpr uint32_t DEFAULT_PRIORITY = 16;
101     static fbl::RefPtr<ExecutionDomain> Create(uint32_t priority = DEFAULT_PRIORITY);
102 
Deactivate()103     void Deactivate() __TA_EXCLUDES(domain_token_) { Deactivate(true); }
DeactivateFromWithinDomain()104     void DeactivateFromWithinDomain() __TA_REQUIRES(domain_token_) { Deactivate(false); }
105 
deactivated()106     bool deactivated() const __TA_NO_THREAD_SAFETY_ANALYSIS {
107         return (deactivated_.load() != 0);
108     }
109 
token()110     const Token& token() __TA_RETURN_CAPABILITY(domain_token_) { return domain_token_; }
111 
112 private:
113     friend class fbl::RefPtr<ExecutionDomain>;
114     friend class Channel;
115     friend class EventSource;
116     friend class Thread;
117     friend class ThreadPool;
118     friend class Timer;
119     friend class WakeupEvent;
120     using DispatchState = EventSource::DispatchState;
121 
122     struct ThreadPoolListTraits {
123         static fbl::DoublyLinkedListNodeState<fbl::RefPtr<ExecutionDomain>>&
node_stateThreadPoolListTraits124             node_state(ExecutionDomain& domain) {
125             return domain.thread_pool_node_state_;
126         }
127     };
128 
129     ExecutionDomain(fbl::RefPtr<ThreadPool> thread_pool, zx::event dispatch_idle_evt);
130     virtual ~ExecutionDomain();
131 
132     void Deactivate(bool sync_dispatch);
133 
134     fbl::RefPtr<ThreadPool> GetThreadPool() __TA_EXCLUDES(sources_lock_);
135     zx_status_t AddEventSource(fbl::RefPtr<EventSource>&& source)
136         __TA_EXCLUDES(sources_lock_);
137     void RemoveEventSource(EventSource* source)
138         __TA_EXCLUDES(sources_lock_);
139 
140     // Add an event source which has pending work to the queue of pending
141     // work for this owner.  Returns true if this was the first pending job
142     // added to the queue, and therefor the calling thread is responsible
143     // for processing the contents of the queue now.
144     bool AddPendingWork(EventSource* source)
145         __TA_REQUIRES(source->obj_lock_) __TA_EXCLUDES(sources_lock_);
146 
147     // Attempt to remove an event source from this owner's pending work
148     // list.  Returns true if the source was a member of the list and was
149     // removed, false otherwise.
150     bool RemovePendingWork(EventSource* source)
151         __TA_REQUIRES(source->obj_lock_) __TA_EXCLUDES(sources_lock_);
152 
153     // Process the pending work queue.
154     void DispatchPendingWork();
155 
156     fbl::Mutex sources_lock_;
157     Token domain_token_;
158     std::atomic<uint32_t> deactivated_ __TA_GUARDED(sources_lock_);
159     bool dispatch_in_progress_ __TA_GUARDED(sources_lock_) = false;
160     bool dispatch_sync_in_progress_ __TA_GUARDED(sources_lock_) = false;
161     fbl::RefPtr<ThreadPool> thread_pool_ __TA_GUARDED(sources_lock_);
162     zx::event dispatch_idle_evt_;
163 
164     // The list of all sources bound to us, as well as the sources which are
165     // currently waiting to be dispatched.
166     fbl::DoublyLinkedList<fbl::RefPtr<EventSource>,
167                            EventSource::SourcesListTraits> sources_
168                                __TA_GUARDED(sources_lock_);
169     fbl::DoublyLinkedList<fbl::RefPtr<EventSource>,
170                            EventSource::PendingWorkListTraits> pending_work_
171                                __TA_GUARDED(sources_lock_);
172 
173     // Node state for existing in our thread pool's execution domain list.
174     fbl::DoublyLinkedListNodeState<fbl::RefPtr<ExecutionDomain>> thread_pool_node_state_;
175 };
176 
177 // A helper macro which can ease some of the namespace pain of establishing the
178 // fact that you are running in a particular execution domain.  Instead of
179 // saying something like this...
180 //
181 // ::dispatcher::ExecutionDomain::ScopedToken token(my_domain_->token());
182 //
183 // One can also say...
184 //
185 // OBTAIN_EXECUTION_DOMAIN_TOKEN(token, my_domain_);
186 //
187 #define OBTAIN_EXECUTION_DOMAIN_TOKEN(_sym_name, _exe_domain) \
188     ::dispatcher::ExecutionDomain::ScopedToken _sym_name((_exe_domain)->token())
189 
190 }  // namespace dispatcher
191