security/manager/ssl/src/nsNSSShutDown.h

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial