nsprpub/pr/src/threads/prcmon.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "primpl.h"
michael@0 7
michael@0 8 #include <stdlib.h>
michael@0 9 #include <stddef.h>
michael@0 10
michael@0 11 /* Lock used to lock the monitor cache */
michael@0 12 #ifdef _PR_NO_PREEMPT
michael@0 13 #define _PR_NEW_LOCK_MCACHE()
michael@0 14 #define _PR_DESTROY_LOCK_MCACHE()
michael@0 15 #define _PR_LOCK_MCACHE()
michael@0 16 #define _PR_UNLOCK_MCACHE()
michael@0 17 #else
michael@0 18 #ifdef _PR_LOCAL_THREADS_ONLY
michael@0 19 #define _PR_NEW_LOCK_MCACHE()
michael@0 20 #define _PR_DESTROY_LOCK_MCACHE()
michael@0 21 #define _PR_LOCK_MCACHE() { PRIntn _is; _PR_INTSOFF(_is)
michael@0 22 #define _PR_UNLOCK_MCACHE() _PR_INTSON(_is); }
michael@0 23 #else
michael@0 24 PRLock *_pr_mcacheLock;
michael@0 25 #define _PR_NEW_LOCK_MCACHE() (_pr_mcacheLock = PR_NewLock())
michael@0 26 #define _PR_DESTROY_LOCK_MCACHE() \
michael@0 27 PR_BEGIN_MACRO \
michael@0 28 if (_pr_mcacheLock) { \
michael@0 29 PR_DestroyLock(_pr_mcacheLock); \
michael@0 30 _pr_mcacheLock = NULL; \
michael@0 31 } \
michael@0 32 PR_END_MACRO
michael@0 33 #define _PR_LOCK_MCACHE() PR_Lock(_pr_mcacheLock)
michael@0 34 #define _PR_UNLOCK_MCACHE() PR_Unlock(_pr_mcacheLock)
michael@0 35 #endif
michael@0 36 #endif
michael@0 37
michael@0 38 /************************************************************************/
michael@0 39
michael@0 40 typedef struct MonitorCacheEntryStr MonitorCacheEntry;
michael@0 41
michael@0 42 struct MonitorCacheEntryStr {
michael@0 43 MonitorCacheEntry* next;
michael@0 44 void* address;
michael@0 45 PRMonitor* mon;
michael@0 46 long cacheEntryCount;
michael@0 47 };
michael@0 48
michael@0 49 /*
michael@0 50 ** An array of MonitorCacheEntry's, plus a pointer to link these
michael@0 51 ** arrays together.
michael@0 52 */
michael@0 53
michael@0 54 typedef struct MonitorCacheEntryBlockStr MonitorCacheEntryBlock;
michael@0 55
michael@0 56 struct MonitorCacheEntryBlockStr {
michael@0 57 MonitorCacheEntryBlock* next;
michael@0 58 MonitorCacheEntry entries[1];
michael@0 59 };
michael@0 60
michael@0 61 static PRUint32 hash_mask;
michael@0 62 static PRUintn num_hash_buckets;
michael@0 63 static PRUintn num_hash_buckets_log2;
michael@0 64 static MonitorCacheEntry **hash_buckets;
michael@0 65 static MonitorCacheEntry *free_entries;
michael@0 66 static PRUintn num_free_entries;
michael@0 67 static PRBool expanding;
michael@0 68 static MonitorCacheEntryBlock *mcache_blocks;
michael@0 69
michael@0 70 static void (*OnMonitorRecycle)(void *address);
michael@0 71
michael@0 72 #define HASH(address) \
michael@0 73 ((PRUint32) ( ((PRUptrdiff)(address) >> 2) ^ \
michael@0 74 ((PRUptrdiff)(address) >> 10) ) \
michael@0 75 & hash_mask)
michael@0 76
michael@0 77 /*
michael@0 78 ** Expand the monitor cache. This grows the hash buckets and allocates a
michael@0 79 ** new chunk of cache entries and throws them on the free list. We keep
michael@0 80 ** as many hash buckets as there are entries.
michael@0 81 **
michael@0 82 ** Because we call malloc and malloc may need the monitor cache, we must
michael@0 83 ** ensure that there are several free monitor cache entries available for
michael@0 84 ** malloc to get. FREE_THRESHOLD is used to prevent monitor cache
michael@0 85 ** starvation during monitor cache expansion.
michael@0 86 */
michael@0 87
michael@0 88 #define FREE_THRESHOLD 5
michael@0 89
michael@0 90 static PRStatus ExpandMonitorCache(PRUintn new_size_log2)
michael@0 91 {
michael@0 92 MonitorCacheEntry **old_hash_buckets, *p;
michael@0 93 PRUintn i, entries, old_num_hash_buckets, added;
michael@0 94 MonitorCacheEntry **new_hash_buckets;
michael@0 95 MonitorCacheEntryBlock *new_block;
michael@0 96
michael@0 97 entries = 1L << new_size_log2;
michael@0 98
michael@0 99 /*
michael@0 100 ** Expand the monitor-cache-entry free list
michael@0 101 */
michael@0 102 new_block = (MonitorCacheEntryBlock*)
michael@0 103 PR_CALLOC(sizeof(MonitorCacheEntryBlock)
michael@0 104 + (entries - 1) * sizeof(MonitorCacheEntry));
michael@0 105 if (NULL == new_block) return PR_FAILURE;
michael@0 106
michael@0 107 /*
michael@0 108 ** Allocate system monitors for the new monitor cache entries. If we
michael@0 109 ** run out of system monitors, break out of the loop.
michael@0 110 */
michael@0 111 for (i = 0, p = new_block->entries; i < entries; i++, p++) {
michael@0 112 p->mon = PR_NewMonitor();
michael@0 113 if (!p->mon)
michael@0 114 break;
michael@0 115 }
michael@0 116 added = i;
michael@0 117 if (added != entries) {
michael@0 118 MonitorCacheEntryBlock *realloc_block;
michael@0 119
michael@0 120 if (added == 0) {
michael@0 121 /* Totally out of system monitors. Lossage abounds */
michael@0 122 PR_DELETE(new_block);
michael@0 123 return PR_FAILURE;
michael@0 124 }
michael@0 125
michael@0 126 /*
michael@0 127 ** We were able to allocate some of the system monitors. Use
michael@0 128 ** realloc to shrink down the new_block memory. If that fails,
michael@0 129 ** carry on with the too-large new_block.
michael@0 130 */
michael@0 131 realloc_block = (MonitorCacheEntryBlock*)
michael@0 132 PR_REALLOC(new_block, sizeof(MonitorCacheEntryBlock)
michael@0 133 + (added - 1) * sizeof(MonitorCacheEntry));
michael@0 134 if (realloc_block)
michael@0 135 new_block = realloc_block;
michael@0 136 }
michael@0 137
michael@0 138 /*
michael@0 139 ** Now that we have allocated all of the system monitors, build up
michael@0 140 ** the new free list. We can just update the free_list because we own
michael@0 141 ** the mcache-lock and we aren't calling anyone who might want to use
michael@0 142 ** it.
michael@0 143 */
michael@0 144 for (i = 0, p = new_block->entries; i < added - 1; i++, p++)
michael@0 145 p->next = p + 1;
michael@0 146 p->next = free_entries;
michael@0 147 free_entries = new_block->entries;
michael@0 148 num_free_entries += added;
michael@0 149 new_block->next = mcache_blocks;
michael@0 150 mcache_blocks = new_block;
michael@0 151
michael@0 152 /* Try to expand the hash table */
michael@0 153 new_hash_buckets = (MonitorCacheEntry**)
michael@0 154 PR_CALLOC(entries * sizeof(MonitorCacheEntry*));
michael@0 155 if (NULL == new_hash_buckets) {
michael@0 156 /*
michael@0 157 ** Partial lossage. In this situation we don't get any more hash
michael@0 158 ** buckets, which just means that the table lookups will take
michael@0 159 ** longer. This is bad, but not fatal
michael@0 160 */
michael@0 161 PR_LOG(_pr_cmon_lm, PR_LOG_WARNING,
michael@0 162 ("unable to grow monitor cache hash buckets"));
michael@0 163 return PR_SUCCESS;
michael@0 164 }
michael@0 165
michael@0 166 /*
michael@0 167 ** Compute new hash mask value. This value is used to mask an address
michael@0 168 ** until it's bits are in the right spot for indexing into the hash
michael@0 169 ** table.
michael@0 170 */
michael@0 171 hash_mask = entries - 1;
michael@0 172
michael@0 173 /*
michael@0 174 ** Expand the hash table. We have to rehash everything in the old
michael@0 175 ** table into the new table.
michael@0 176 */
michael@0 177 old_hash_buckets = hash_buckets;
michael@0 178 old_num_hash_buckets = num_hash_buckets;
michael@0 179 for (i = 0; i < old_num_hash_buckets; i++) {
michael@0 180 p = old_hash_buckets[i];
michael@0 181 while (p) {
michael@0 182 MonitorCacheEntry *next = p->next;
michael@0 183
michael@0 184 /* Hash based on new table size, and then put p in the new table */
michael@0 185 PRUintn hash = HASH(p->address);
michael@0 186 p->next = new_hash_buckets[hash];
michael@0 187 new_hash_buckets[hash] = p;
michael@0 188
michael@0 189 p = next;
michael@0 190 }
michael@0 191 }
michael@0 192
michael@0 193 /*
michael@0 194 ** Switch over to new hash table and THEN call free of the old
michael@0 195 ** table. Since free might re-enter _pr_mcache_lock, things would
michael@0 196 ** break terribly if it used the old hash table.
michael@0 197 */
michael@0 198 hash_buckets = new_hash_buckets;
michael@0 199 num_hash_buckets = entries;
michael@0 200 num_hash_buckets_log2 = new_size_log2;
michael@0 201 PR_DELETE(old_hash_buckets);
michael@0 202
michael@0 203 PR_LOG(_pr_cmon_lm, PR_LOG_NOTICE,
michael@0 204 ("expanded monitor cache to %d (buckets %d)",
michael@0 205 num_free_entries, entries));
michael@0 206
michael@0 207 return PR_SUCCESS;
michael@0 208 } /* ExpandMonitorCache */
michael@0 209
michael@0 210 /*
michael@0 211 ** Lookup a monitor cache entry by address. Return a pointer to the
michael@0 212 ** pointer to the monitor cache entry on success, null on failure.
michael@0 213 */
michael@0 214 static MonitorCacheEntry **LookupMonitorCacheEntry(void *address)
michael@0 215 {
michael@0 216 PRUintn hash;
michael@0 217 MonitorCacheEntry **pp, *p;
michael@0 218
michael@0 219 hash = HASH(address);
michael@0 220 pp = hash_buckets + hash;
michael@0 221 while ((p = *pp) != 0) {
michael@0 222 if (p->address == address) {
michael@0 223 if (p->cacheEntryCount > 0)
michael@0 224 return pp;
michael@0 225 return NULL;
michael@0 226 }
michael@0 227 pp = &p->next;
michael@0 228 }
michael@0 229 return NULL;
michael@0 230 }
michael@0 231
michael@0 232 /*
michael@0 233 ** Try to create a new cached monitor. If it's already in the cache,
michael@0 234 ** great - return it. Otherwise get a new free cache entry and set it
michael@0 235 ** up. If the cache free space is getting low, expand the cache.
michael@0 236 */
michael@0 237 static PRMonitor *CreateMonitor(void *address)
michael@0 238 {
michael@0 239 PRUintn hash;
michael@0 240 MonitorCacheEntry **pp, *p;
michael@0 241
michael@0 242 hash = HASH(address);
michael@0 243 pp = hash_buckets + hash;
michael@0 244 while ((p = *pp) != 0) {
michael@0 245 if (p->address == address) goto gotit;
michael@0 246
michael@0 247 pp = &p->next;
michael@0 248 }
michael@0 249
michael@0 250 /* Expand the monitor cache if we have run out of free slots in the table */
michael@0 251 if (num_free_entries < FREE_THRESHOLD) {
michael@0 252 /* Expand monitor cache */
michael@0 253
michael@0 254 /*
michael@0 255 ** This function is called with the lock held. So what's the 'expanding'
michael@0 256 ** boolean all about? Seems a bit redundant.
michael@0 257 */
michael@0 258 if (!expanding) {
michael@0 259 PRStatus rv;
michael@0 260
michael@0 261 expanding = PR_TRUE;
michael@0 262 rv = ExpandMonitorCache(num_hash_buckets_log2 + 1);
michael@0 263 expanding = PR_FALSE;
michael@0 264 if (PR_FAILURE == rv) return NULL;
michael@0 265
michael@0 266 /* redo the hash because it'll be different now */
michael@0 267 hash = HASH(address);
michael@0 268 } else {
michael@0 269 /*
michael@0 270 ** We are in process of expanding and we need a cache
michael@0 271 ** monitor. Make sure we have enough!
michael@0 272 */
michael@0 273 PR_ASSERT(num_free_entries > 0);
michael@0 274 }
michael@0 275 }
michael@0 276
michael@0 277 /* Make a new monitor */
michael@0 278 p = free_entries;
michael@0 279 free_entries = p->next;
michael@0 280 num_free_entries--;
michael@0 281 if (OnMonitorRecycle && p->address)
michael@0 282 OnMonitorRecycle(p->address);
michael@0 283 p->address = address;
michael@0 284 p->next = hash_buckets[hash];
michael@0 285 hash_buckets[hash] = p;
michael@0 286 PR_ASSERT(p->cacheEntryCount == 0);
michael@0 287
michael@0 288 gotit:
michael@0 289 p->cacheEntryCount++;
michael@0 290 return p->mon;
michael@0 291 }
michael@0 292
michael@0 293 /*
michael@0 294 ** Initialize the monitor cache
michael@0 295 */
michael@0 296 void _PR_InitCMon(void)
michael@0 297 {
michael@0 298 _PR_NEW_LOCK_MCACHE();
michael@0 299 ExpandMonitorCache(3);
michael@0 300 }
michael@0 301
michael@0 302 /*
michael@0 303 ** Destroy the monitor cache
michael@0 304 */
michael@0 305 void _PR_CleanupCMon(void)
michael@0 306 {
michael@0 307 _PR_DESTROY_LOCK_MCACHE();
michael@0 308
michael@0 309 while (free_entries) {
michael@0 310 PR_DestroyMonitor(free_entries->mon);
michael@0 311 free_entries = free_entries->next;
michael@0 312 }
michael@0 313 num_free_entries = 0;
michael@0 314
michael@0 315 while (mcache_blocks) {
michael@0 316 MonitorCacheEntryBlock *block;
michael@0 317
michael@0 318 block = mcache_blocks;
michael@0 319 mcache_blocks = block->next;
michael@0 320 PR_DELETE(block);
michael@0 321 }
michael@0 322
michael@0 323 PR_DELETE(hash_buckets);
michael@0 324 hash_mask = 0;
michael@0 325 num_hash_buckets = 0;
michael@0 326 num_hash_buckets_log2 = 0;
michael@0 327
michael@0 328 expanding = PR_FALSE;
michael@0 329 OnMonitorRecycle = NULL;
michael@0 330 }
michael@0 331
michael@0 332 /*
michael@0 333 ** Create monitor for address. Don't enter the monitor while we have the
michael@0 334 ** mcache locked because we might block!
michael@0 335 */
michael@0 336 PR_IMPLEMENT(PRMonitor*) PR_CEnterMonitor(void *address)
michael@0 337 {
michael@0 338 PRMonitor *mon;
michael@0 339
michael@0 340 if (!_pr_initialized) _PR_ImplicitInitialization();
michael@0 341
michael@0 342 _PR_LOCK_MCACHE();
michael@0 343 mon = CreateMonitor(address);
michael@0 344 _PR_UNLOCK_MCACHE();
michael@0 345
michael@0 346 if (!mon) return NULL;
michael@0 347
michael@0 348 PR_EnterMonitor(mon);
michael@0 349 return mon;
michael@0 350 }
michael@0 351
michael@0 352 PR_IMPLEMENT(PRStatus) PR_CExitMonitor(void *address)
michael@0 353 {
michael@0 354 MonitorCacheEntry **pp, *p;
michael@0 355 PRStatus status = PR_SUCCESS;
michael@0 356
michael@0 357 _PR_LOCK_MCACHE();
michael@0 358 pp = LookupMonitorCacheEntry(address);
michael@0 359 if (pp != NULL) {
michael@0 360 p = *pp;
michael@0 361 if (--p->cacheEntryCount == 0) {
michael@0 362 /*
michael@0 363 ** Nobody is using the system monitor. Put it on the cached free
michael@0 364 ** list. We are safe from somebody trying to use it because we
michael@0 365 ** have the mcache locked.
michael@0 366 */
michael@0 367 p->address = 0; /* defensive move */
michael@0 368 *pp = p->next; /* unlink from hash_buckets */
michael@0 369 p->next = free_entries; /* link into free list */
michael@0 370 free_entries = p;
michael@0 371 num_free_entries++; /* count it as free */
michael@0 372 }
michael@0 373 status = PR_ExitMonitor(p->mon);
michael@0 374 } else {
michael@0 375 status = PR_FAILURE;
michael@0 376 }
michael@0 377 _PR_UNLOCK_MCACHE();
michael@0 378
michael@0 379 return status;
michael@0 380 }
michael@0 381
michael@0 382 PR_IMPLEMENT(PRStatus) PR_CWait(void *address, PRIntervalTime ticks)
michael@0 383 {
michael@0 384 MonitorCacheEntry **pp;
michael@0 385 PRMonitor *mon;
michael@0 386
michael@0 387 _PR_LOCK_MCACHE();
michael@0 388 pp = LookupMonitorCacheEntry(address);
michael@0 389 mon = pp ? ((*pp)->mon) : NULL;
michael@0 390 _PR_UNLOCK_MCACHE();
michael@0 391
michael@0 392 if (mon == NULL)
michael@0 393 return PR_FAILURE;
michael@0 394 return PR_Wait(mon, ticks);
michael@0 395 }
michael@0 396
michael@0 397 PR_IMPLEMENT(PRStatus) PR_CNotify(void *address)
michael@0 398 {
michael@0 399 MonitorCacheEntry **pp;
michael@0 400 PRMonitor *mon;
michael@0 401
michael@0 402 _PR_LOCK_MCACHE();
michael@0 403 pp = LookupMonitorCacheEntry(address);
michael@0 404 mon = pp ? ((*pp)->mon) : NULL;
michael@0 405 _PR_UNLOCK_MCACHE();
michael@0 406
michael@0 407 if (mon == NULL)
michael@0 408 return PR_FAILURE;
michael@0 409 return PR_Notify(mon);
michael@0 410 }
michael@0 411
michael@0 412 PR_IMPLEMENT(PRStatus) PR_CNotifyAll(void *address)
michael@0 413 {
michael@0 414 MonitorCacheEntry **pp;
michael@0 415 PRMonitor *mon;
michael@0 416
michael@0 417 _PR_LOCK_MCACHE();
michael@0 418 pp = LookupMonitorCacheEntry(address);
michael@0 419 mon = pp ? ((*pp)->mon) : NULL;
michael@0 420 _PR_UNLOCK_MCACHE();
michael@0 421
michael@0 422 if (mon == NULL)
michael@0 423 return PR_FAILURE;
michael@0 424 return PR_NotifyAll(mon);
michael@0 425 }
michael@0 426
michael@0 427 PR_IMPLEMENT(void)
michael@0 428 PR_CSetOnMonitorRecycle(void (*callback)(void *address))
michael@0 429 {
michael@0 430 OnMonitorRecycle = callback;
michael@0 431 }

mercurial