1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef CEEE_IE_BROKER_EXECUTORS_MANAGER_DOCS_H_ // Mainly for lint
#define CEEE_IE_BROKER_EXECUTORS_MANAGER_DOCS_H_
/* @page ExecutorsManagerDoc Detailed documentation of the ExecutorsManager.
@section ExecutorsManagerIntro Introduction
The Executors Manager implemented by the ExecutorsManager class,
is used by the CeeeBroker object to instantiate and hold on to
Marshaled proxies of CeeeExecutor COM objects running in other processes.
The complexity lies in the fact that the Broker could send concurrent requests
to access Executor proxies and the creation of new proxies can't be done
synchronously.
So the Executors Manager must block on a request for a new Executor, and all
subsequent requests for the same executor until the asynchronous creation of
the Executor is complete. Once the Executor is completely registered in the
Executors Manager, it can be returned right away when requested (though a wait
may need to be done on a lock to access the map of Executors).
Executors are created to execute code in a given thread (called the
destination thread from now on), so we map the Executor to their associated
destination thread identifier and reuse existing Executors for subsequent
requests to execute in the same destination thread. We also keep an open
handle to the destination thread so that we can wait to it in a separate
Executors Manager @ref ThreadProc "worker thread" and wake up when the
destination thread dies, so that we can clean our map.
@section PublicMethods Public methods
The Executors Manager has has 3 public entry points, @ref GetExecutor,
@ref AddExecutor and @ref RemoveExecutor.
@subsection GetExecutor
The @link ExecutorsManager::GetExecutor GetExecutor @endlink method returns a
reference to a CeeeExecutor COM object (simply called Executor from now on)
for a given destination thread. It first checks for an existing Executor in
the @link ExecutorsManager::executors_ Executors map@endlink, and if it finds
one, it simply returns it (though it must @link ExecutorsManager::lock_ lock
@endlink the access to the Executors map).
If the @link ExecutorsManager::executors_ Executors map@endlink doesn't
already have an Executor for the requested
destination thread, we must first check if we already have a @link
ExecutorsManager::pending_registrations_ pending registration @endlink for that
same destination thread.
As mentioned @ref ExecutorsManagerIntro "above", registration requests can be
pending, since the creation of new Executors can not be done synchronously.
If there are no existing Executor and no pending registration to create one,
we can create a new pending registration (all of this, properly locked so that
we make sure to only start one pending registration for a given destination
thread).
The creation of new Executors is done via the usage of the @link
CeeeExecutorCreator Executor Creator @endlink COM object that is exposed by
a DLL so that it can inject a @link CeeeExecutorCreator::hook_ hook
@endlink in the destination thread to create the Executor. Once the hook is
injected, we can only post to the hook (since sending a message creates
a user-synchronous operation that can't get out of the process, so we won't be
able to register the newly created Executor in the Broker).
Once the Executor Creator posted a message to the hook to instantiate a new
Executor and get it registered in the Broker, the Executors Manager must wait
for the registration to complete. Since the Executors Manager is called by the
Broker, which is instantiated in a multi-threaded apartment, the registration
will happen in a free thread while the thread that made the call to GetExecutor
is blocked, waiting for the pending registration to complete.
At that point, there may be more than one thread calling GetExecutor for the
same destination thread, so they must all wait on the registration complete
event. Also, since there can be more than one destination thread with pending
registrations for a new Executor, there must be multiple events used to signal
the completion of the registration, one for each destination thread that has a
pending request to create a new executor. So the Executors Manager keeps a
@link ExecutorsManager::pending_registrations_ map of destination thread
identifier to registration completion event@endlink.
Once the registration of a new Executor is complete (via a call to @ref
RegisterTabExecutor), the event for the destination thread for which the
Executor is being registered is signaled so that any thread waiting for it can
wake up, get the new Executor from the map, and return it to their caller.
@subsection RegisterExecutor
The @link ExecutorsManager::RegisterExecutor RegisterExecutor @endlink method is
called by the Broker when it receives a request to register a new Executor for a
given destination thread. So the Executors Manager must first confirm that we
didn't already have an Executor for that destination thread, and also confirm
that we actually have a pending request for that destination thread.
@note <b>TODO(mad@chromium.org)</b>: We need to change this so that we
allow the BHO and maybe other modules in the future to preemptively
register Executors without a pending request.
Once this is confirmed, the new Executor is added to the map (again, in a
thread safe way, of course), and the event of the pending request to register
an executor for that destination thread is signaled. We also need to wake up
the @ref ThreadProc "worker thread", waiting on destination thread handles,
(using the @ref update_threads_list_gate_ event) so that the new one can be
added to the list to wait on.
@subsection RemoveExecutor
If for some reason, an executor must remove itself from the Executors Manager's
map, it can do so by calling the @link
ICeeeBrokerRegistrar::UnregisterExecutor UnregisterExecutor @endlink method
on the Broker ICeeeBrokerRegistrar interface which delegates to the
Executors Manager's @link ExecutorsManager::RemoveExecutor RemoveExecutor
@endlink method.
This same method is also called from the Executors Manager's @ref ThreadProc
"worker thread" waiting for the death of the destination thread for which we
have an executor in the map. When the thread dies, we remove the associated
executor from the map.
@subsection Terminate
The @link ExecutorsManager::Terminate Terminate @endlink method is to be called
when we are done with the Executors Manager, and it can clean up and release
all its resources (including stopping the @ref ThreadProc "worker thread" used
to wait for the destination threads to die, by setting the @ref
termination_gate_ event).
@section PrivateMethods Private methods
We use a few private methods to help with the implementation of the public
methods described @ref PublicMethods "above".
@subsection GetThreadHandles
@link ExecutorsManager::GetThreadHandles GetThreadHandles @endlink parses
through our @link ExecutorsManager::executors_ Executors map @endlink to return
the list of handles that the @ref ThreadProc "worker thread" has to wait for.
The caller must provide two pre-allocated arrays, one for thread handles and the
other one for thread identifiers. So we may not be able to return info for
all the threads if there isn't enough room. This is OK since the only caller is
the worker thread procedure (described @ref ThreadProc "below") and it can't
wait for more than a specific number of handles anyway. The arrays must be kept
in sync, so that index i in one of them refers to the same thread as index i in
the other.
@subsection ThreadProc
The @link ExecutorsManager::ThreadProc ThreadProc @endlink is the worker thread
procedure that is used to get the list of destination threads for
which we have an executor in the @link ExecutorsManager::executors_ Executors
map@endlink, and then wait on them to know when they die.
We also need to wait on two events, @ref termination_gate_ for the termination
of the worker thread (set in the Terminate public method described @ref
Terminate "above") and @ref update_threads_list_gate_ warning us that there are
new threads that we must wait on (set in the RegisterExecutor public method
described @ref RegisterExecutor "above").
@section PrivateDataMembers Private data members
@subsection executors_
@link ExecutorsManager::executors_ executors_ @endlink is a map from a
destination thread identifier to a couple made of an Executor interface and a
thread handle. We need to keep an opened thread handle so that we can wait on
it until it dies.
@subsection pending_registrations_
@link ExecutorsManager::pending_registrations_ pending_registrations_ @endlink
is another map of destination thread identifier, but this one holds on to
synchronization events that we will set once the registration of an executor
for the given destination thread is complete. Since there may be more than one
thread waiting on this signal, and we use the signal only once, we create it as
a manual reset event.
@subsection thread_
Of course, we need to keep a @link ExecutorsManager::thread_ handle @endlink on
our worker thread.
@subsection update_threads_list_gate_
This @link ExecutorsManager::update_threads_list_gate_ event @endlink is used
to wake the worker thread so that it refreshes its list of thread handles that
it waits on. It is set from the RegisterExecutor public method described
@ref RegisterExecutor "above".
@subsection termination_gate_
This other @link ExecutorsManager::termination_gate_ event @endlink is set from
the Terminate public method described @ref Terminate "above" to notify the
worker thread to terminate itself.
@subsection lock_
Since the Executors Manager can be called from many different threads, it must
protect the access to its data (and atomize certain operations) with this @link
ExecutorsManager::lock_ lock @endlink.
*/
#endif // CEEE_IE_BROKER_EXECUTORS_MANAGER_DOCS_H_
|