|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #ifndef _INC_NSSShutDown_H |
|
6 #define _INC_NSSShutDown_H |
|
7 |
|
8 #include "nscore.h" |
|
9 #include "nspr.h" |
|
10 #include "pldhash.h" |
|
11 #include "mozilla/CondVar.h" |
|
12 #include "mozilla/Mutex.h" |
|
13 |
|
14 class nsNSSShutDownObject; |
|
15 class nsOnPK11LogoutCancelObject; |
|
16 |
|
17 // Singleton, owner by nsNSSShutDownList |
|
18 class nsNSSActivityState |
|
19 { |
|
20 public: |
|
21 nsNSSActivityState(); |
|
22 ~nsNSSActivityState(); |
|
23 |
|
24 // Call enter/leave when PSM enters a scope during which |
|
25 // shutting down NSS is prohibited. |
|
26 void enter(); |
|
27 void leave(); |
|
28 |
|
29 // Call enter/leave when PSM is about to show a UI |
|
30 // while still holding resources. |
|
31 void enterBlockingUIState(); |
|
32 void leaveBlockingUIState(); |
|
33 |
|
34 // Is the activity aware of any blocking PSM UI currently shown? |
|
35 bool isBlockingUIActive(); |
|
36 |
|
37 // Is it forbidden to bring up an UI while holding resources? |
|
38 bool isUIForbidden(); |
|
39 |
|
40 // Check whether setting the current thread restriction is possible. |
|
41 // If it is possible, and the "do_it_for_real" flag is used, |
|
42 // the state tracking will have ensured that we will stay in this state. |
|
43 // As of writing, this includes forbidding PSM UI. |
|
44 enum RealOrTesting {test_only, do_it_for_real}; |
|
45 bool ifPossibleDisallowUI(RealOrTesting rot); |
|
46 |
|
47 // Notify the state tracking that going to the restricted state is |
|
48 // no longer planned. |
|
49 // As of writing, this includes clearing the "PSM UI forbidden" flag. |
|
50 void allowUI(); |
|
51 |
|
52 // If currently no UI is shown, wait for all activity to stop, |
|
53 // and block any other thread on entering relevant PSM code. |
|
54 PRStatus restrictActivityToCurrentThread(); |
|
55 |
|
56 // Go back to normal state. |
|
57 void releaseCurrentThreadActivityRestriction(); |
|
58 |
|
59 private: |
|
60 // The lock protecting all our member variables. |
|
61 mozilla::Mutex mNSSActivityStateLock; |
|
62 |
|
63 // The activity variable, bound to our lock, |
|
64 // used either to signal the activity counter reaches zero, |
|
65 // or a thread restriction has been released. |
|
66 mozilla::CondVar mNSSActivityChanged; |
|
67 |
|
68 // The number of active scopes holding resources. |
|
69 int mNSSActivityCounter; |
|
70 |
|
71 // The number of scopes holding resources while blocked |
|
72 // showing an UI. |
|
73 int mBlockingUICounter; |
|
74 |
|
75 // Whether bringing up UI is currently forbidden |
|
76 bool mIsUIForbidden; |
|
77 |
|
78 // nullptr means "no restriction" |
|
79 // if not null, activity is only allowed on that thread |
|
80 PRThread* mNSSRestrictedThread; |
|
81 }; |
|
82 |
|
83 // Helper class that automatically enters/leaves the global activity state |
|
84 class nsNSSShutDownPreventionLock |
|
85 { |
|
86 public: |
|
87 nsNSSShutDownPreventionLock(); |
|
88 ~nsNSSShutDownPreventionLock(); |
|
89 }; |
|
90 |
|
91 // Helper class that automatically enters/leaves the global UI tracking |
|
92 class nsPSMUITracker |
|
93 { |
|
94 public: |
|
95 nsPSMUITracker(); |
|
96 ~nsPSMUITracker(); |
|
97 |
|
98 bool isUIForbidden(); |
|
99 }; |
|
100 |
|
101 // Singleton, used by nsNSSComponent to track the list of PSM objects, |
|
102 // which hold NSS resources and support the "early cleanup mechanism". |
|
103 class nsNSSShutDownList |
|
104 { |
|
105 public: |
|
106 ~nsNSSShutDownList(); |
|
107 |
|
108 static nsNSSShutDownList *construct(); |
|
109 |
|
110 // track instances that support early cleanup |
|
111 static void remember(nsNSSShutDownObject *o); |
|
112 static void forget(nsNSSShutDownObject *o); |
|
113 |
|
114 // track instances that would like notification when |
|
115 // a PK11 logout operation is performed. |
|
116 static void remember(nsOnPK11LogoutCancelObject *o); |
|
117 static void forget(nsOnPK11LogoutCancelObject *o); |
|
118 |
|
119 // track the creation and destruction of SSL sockets |
|
120 // performed by clients using PSM services |
|
121 static void trackSSLSocketCreate(); |
|
122 static void trackSSLSocketClose(); |
|
123 static bool areSSLSocketsActive(); |
|
124 |
|
125 // Are we able to do the early cleanup? |
|
126 // Returns failure if at the current time "early cleanup" is not possible. |
|
127 bool isUIActive(); |
|
128 |
|
129 // If possible to do "early cleanup" at the current time, remember that we want to |
|
130 // do it, and disallow actions that would change the possibility. |
|
131 bool ifPossibleDisallowUI(); |
|
132 |
|
133 // Notify that it is no longer planned to do the "early cleanup". |
|
134 void allowUI(); |
|
135 |
|
136 // Do the "early cleanup", if possible. |
|
137 nsresult evaporateAllNSSResources(); |
|
138 |
|
139 // PSM has been asked to log out of a token. |
|
140 // Notify all registered instances that want to react to that event. |
|
141 nsresult doPK11Logout(); |
|
142 |
|
143 static nsNSSActivityState *getActivityState() |
|
144 { |
|
145 return singleton ? &singleton->mActivityState : nullptr; |
|
146 } |
|
147 |
|
148 private: |
|
149 nsNSSShutDownList(); |
|
150 static PLDHashOperator |
|
151 evaporateAllNSSResourcesHelper(PLDHashTable *table, PLDHashEntryHdr *hdr, |
|
152 uint32_t number, void *arg); |
|
153 |
|
154 static PLDHashOperator |
|
155 doPK11LogoutHelper(PLDHashTable *table, PLDHashEntryHdr *hdr, |
|
156 uint32_t number, void *arg); |
|
157 protected: |
|
158 mozilla::Mutex mListLock; |
|
159 static nsNSSShutDownList *singleton; |
|
160 PLDHashTable mObjects; |
|
161 uint32_t mActiveSSLSockets; |
|
162 PLDHashTable mPK11LogoutCancelObjects; |
|
163 nsNSSActivityState mActivityState; |
|
164 }; |
|
165 |
|
166 /* |
|
167 A class deriving from nsNSSShutDownObject will have its instances |
|
168 automatically tracked in a list. However, it must follow some rules |
|
169 to assure correct behaviour. |
|
170 |
|
171 The tricky part is that it is not possible to call virtual |
|
172 functions from a destructor. |
|
173 |
|
174 The deriving class must override virtualDestroyNSSReference(). |
|
175 Within this function, it should clean up all resources held to NSS. |
|
176 The function will be called by the global list, if it is time to |
|
177 shut down NSS before all references have been freed. |
|
178 |
|
179 The same code that goes into virtualDestroyNSSReference must |
|
180 also be called from the destructor of the deriving class, |
|
181 which is the standard cleanup (not called from the tracking list). |
|
182 |
|
183 Because of that duplication, it is suggested to implement a |
|
184 function destructorSafeDestroyNSSReference() in the deriving |
|
185 class, and make the implementation of virtualDestroyNSSReference() |
|
186 call destructorSafeDestroyNSSReference(). |
|
187 |
|
188 The destructor of the derived class must prevent NSS shutdown on |
|
189 another thread by acquiring an nsNSSShutDownPreventionLock. It must |
|
190 then check to see if NSS has already been shut down by calling |
|
191 isAlreadyShutDown(). If NSS has not been shut down, the destructor |
|
192 must then call destructorSafeDestroyNSSReference() and then |
|
193 shutdown(calledFromObject). The second call will deregister with |
|
194 the tracking list, to ensure no additional attempt to free the resources |
|
195 will be made. |
|
196 |
|
197 destructorSafeDestroyNSSReference() does not need to acquire an |
|
198 nsNSSShutDownPreventionLock or check isAlreadyShutDown() as long as it |
|
199 is only called by the destructor that has already acquired the lock and |
|
200 checked for shutdown or by the NSS shutdown code itself (which acquires |
|
201 the same lock and checks if objects it cleans up have already cleaned |
|
202 up themselves). |
|
203 |
|
204 destructorSafeDestroyNSSReference() MUST NOT cause any other |
|
205 nsNSSShutDownObject to be deconstructed. Doing so can cause |
|
206 unsupported concurrent operations on the hash table in the |
|
207 nsNSSShutDownList. |
|
208 |
|
209 class derivedClass : public nsISomeInterface, |
|
210 public nsNSSShutDownObject |
|
211 { |
|
212 virtual void virtualDestroyNSSReference() |
|
213 { |
|
214 destructorSafeDestroyNSSReference(); |
|
215 } |
|
216 |
|
217 void destructorSafeDestroyNSSReference() |
|
218 { |
|
219 // clean up all NSS resources here |
|
220 } |
|
221 |
|
222 virtual ~derivedClass() |
|
223 { |
|
224 nsNSSShutDownPreventionLock locker; |
|
225 if (isAlreadyShutDown()) { |
|
226 return; |
|
227 } |
|
228 destructorSafeDestroyNSSReference(); |
|
229 shutdown(calledFromObject); |
|
230 } |
|
231 |
|
232 NS_IMETHODIMP doSomething() |
|
233 { |
|
234 if (isAlreadyShutDown()) |
|
235 return NS_ERROR_NOT_AVAILABLE; |
|
236 |
|
237 // use the NSS resources and do something |
|
238 } |
|
239 }; |
|
240 */ |
|
241 |
|
242 class nsNSSShutDownObject |
|
243 { |
|
244 public: |
|
245 |
|
246 enum CalledFromType {calledFromList, calledFromObject}; |
|
247 |
|
248 nsNSSShutDownObject() |
|
249 { |
|
250 mAlreadyShutDown = false; |
|
251 nsNSSShutDownList::remember(this); |
|
252 } |
|
253 |
|
254 virtual ~nsNSSShutDownObject() |
|
255 { |
|
256 // the derived class must call |
|
257 // shutdown(calledFromObject); |
|
258 // in its destructor |
|
259 } |
|
260 |
|
261 void shutdown(CalledFromType calledFrom) |
|
262 { |
|
263 if (!mAlreadyShutDown) { |
|
264 if (calledFromObject == calledFrom) { |
|
265 nsNSSShutDownList::forget(this); |
|
266 } |
|
267 if (calledFromList == calledFrom) { |
|
268 virtualDestroyNSSReference(); |
|
269 } |
|
270 mAlreadyShutDown = true; |
|
271 } |
|
272 } |
|
273 |
|
274 bool isAlreadyShutDown() { return mAlreadyShutDown; } |
|
275 |
|
276 protected: |
|
277 virtual void virtualDestroyNSSReference() = 0; |
|
278 private: |
|
279 volatile bool mAlreadyShutDown; |
|
280 }; |
|
281 |
|
282 class nsOnPK11LogoutCancelObject |
|
283 { |
|
284 public: |
|
285 nsOnPK11LogoutCancelObject() |
|
286 :mIsLoggedOut(false) |
|
287 { |
|
288 nsNSSShutDownList::remember(this); |
|
289 } |
|
290 |
|
291 virtual ~nsOnPK11LogoutCancelObject() |
|
292 { |
|
293 nsNSSShutDownList::forget(this); |
|
294 } |
|
295 |
|
296 void logout() |
|
297 { |
|
298 // We do not care for a race condition. |
|
299 // Once the bool arrived at false, |
|
300 // later calls to isPK11LoggedOut() will see it. |
|
301 // This is a one-time change from 0 to 1. |
|
302 |
|
303 mIsLoggedOut = true; |
|
304 } |
|
305 |
|
306 bool isPK11LoggedOut() |
|
307 { |
|
308 return mIsLoggedOut; |
|
309 } |
|
310 |
|
311 private: |
|
312 volatile bool mIsLoggedOut; |
|
313 }; |
|
314 |
|
315 #endif |