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.

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

mercurial