LCOV - code coverage report
Current view: top level - nsprpub/pr/src/threads - prcmon.c (source / functions) Hit Total Coverage
Test: output.info Lines: 34 148 23.0 %
Date: 2017-07-14 16:53:18 Functions: 2 5 40.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #include "primpl.h"
       7             : 
       8             : #include <stdlib.h>
       9             : #include <stddef.h>
      10             : 
      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
      37             : 
      38             : /************************************************************************/
      39             : 
      40             : typedef struct MonitorCacheEntryStr MonitorCacheEntry;
      41             : 
      42             : struct MonitorCacheEntryStr {
      43             :     MonitorCacheEntry*  next;
      44             :     void*               address;
      45             :     PRMonitor*          mon;
      46             :     long                cacheEntryCount;
      47             : };
      48             : 
      49             : /*
      50             : ** An array of MonitorCacheEntry's, plus a pointer to link these
      51             : ** arrays together.
      52             : */
      53             : 
      54             : typedef struct MonitorCacheEntryBlockStr MonitorCacheEntryBlock;
      55             : 
      56             : struct MonitorCacheEntryBlockStr {
      57             :     MonitorCacheEntryBlock* next;
      58             :     MonitorCacheEntry entries[1];
      59             : };
      60             : 
      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;
      69             : 
      70             : static void (*OnMonitorRecycle)(void *address);
      71             : 
      72             : #define HASH(address)                               \
      73             :     ((PRUint32) ( ((PRUptrdiff)(address) >> 2) ^    \
      74             :                   ((PRUptrdiff)(address) >> 10) )   \
      75             :      & hash_mask)
      76             : 
      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             : */
      87             : 
      88             : #define FREE_THRESHOLD  5
      89             : 
      90           3 : 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;
      96             : 
      97           3 :     entries = 1L << new_size_log2;
      98             : 
      99             :     /*
     100             :     ** Expand the monitor-cache-entry free list
     101             :     */
     102           3 :     new_block = (MonitorCacheEntryBlock*)
     103           3 :         PR_CALLOC(sizeof(MonitorCacheEntryBlock)
     104             :         + (entries - 1) * sizeof(MonitorCacheEntry));
     105           3 :     if (NULL == new_block) return PR_FAILURE;
     106             : 
     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          27 :     for (i = 0, p = new_block->entries; i < entries; i++, p++) {
     112          24 :         p->mon = PR_NewMonitor();
     113          24 :         if (!p->mon)
     114           0 :             break;
     115             :     }
     116           3 :     added = i;
     117           3 :     if (added != entries) {
     118             :         MonitorCacheEntryBlock *realloc_block;
     119             : 
     120           0 :         if (added == 0) {
     121             :             /* Totally out of system monitors. Lossage abounds */
     122           0 :             PR_DELETE(new_block);
     123           0 :             return PR_FAILURE;
     124             :         }
     125             : 
     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           0 :         realloc_block = (MonitorCacheEntryBlock*)
     132           0 :             PR_REALLOC(new_block, sizeof(MonitorCacheEntryBlock)
     133             :             + (added - 1) * sizeof(MonitorCacheEntry));
     134           0 :         if (realloc_block)
     135           0 :             new_block = realloc_block;
     136             :     }
     137             : 
     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          24 :     for (i = 0, p = new_block->entries; i < added - 1; i++, p++)
     145          21 :         p->next = p + 1;
     146           3 :     p->next = free_entries;
     147           3 :     free_entries = new_block->entries;
     148           3 :     num_free_entries += added;
     149           3 :     new_block->next = mcache_blocks;
     150           3 :     mcache_blocks = new_block;
     151             : 
     152             :     /* Try to expand the hash table */
     153           3 :     new_hash_buckets = (MonitorCacheEntry**)
     154           3 :         PR_CALLOC(entries * sizeof(MonitorCacheEntry*));
     155           3 :     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           0 :         PR_LOG(_pr_cmon_lm, PR_LOG_WARNING,
     162             :                ("unable to grow monitor cache hash buckets"));
     163           0 :         return PR_SUCCESS;
     164             :     }
     165             : 
     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           3 :     hash_mask = entries - 1;
     172             : 
     173             :     /*
     174             :     ** Expand the hash table. We have to rehash everything in the old
     175             :     ** table into the new table.
     176             :     */
     177           3 :     old_hash_buckets = hash_buckets;
     178           3 :     old_num_hash_buckets = num_hash_buckets;
     179           3 :     for (i = 0; i < old_num_hash_buckets; i++) {
     180           0 :         p = old_hash_buckets[i];
     181           0 :         while (p) {
     182           0 :             MonitorCacheEntry *next = p->next;
     183             : 
     184             :             /* Hash based on new table size, and then put p in the new table */
     185           0 :             PRUintn hash = HASH(p->address);
     186           0 :             p->next = new_hash_buckets[hash];
     187           0 :             new_hash_buckets[hash] = p;
     188             : 
     189           0 :             p = next;
     190             :         }
     191             :     }
     192             : 
     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           3 :     hash_buckets = new_hash_buckets;
     199           3 :     num_hash_buckets = entries;
     200           3 :     num_hash_buckets_log2 = new_size_log2;
     201           3 :     PR_DELETE(old_hash_buckets);
     202             : 
     203           3 :     PR_LOG(_pr_cmon_lm, PR_LOG_NOTICE,
     204             :            ("expanded monitor cache to %d (buckets %d)",
     205             :             num_free_entries, entries));
     206             : 
     207           3 :     return PR_SUCCESS;
     208             : }  /* ExpandMonitorCache */
     209             : 
     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           0 : static MonitorCacheEntry **LookupMonitorCacheEntry(void *address)
     215             : {
     216             :     PRUintn hash;
     217             :     MonitorCacheEntry **pp, *p;
     218             : 
     219           0 :     hash = HASH(address);
     220           0 :     pp = hash_buckets + hash;
     221           0 :     while ((p = *pp) != 0) {
     222           0 :         if (p->address == address) {
     223           0 :             if (p->cacheEntryCount > 0)
     224           0 :                 return pp;
     225           0 :             return NULL;
     226             :         }
     227           0 :         pp = &p->next;
     228             :     }
     229           0 :     return NULL;
     230             : }
     231             : 
     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           0 : static PRMonitor *CreateMonitor(void *address)
     238             : {
     239             :     PRUintn hash;
     240             :     MonitorCacheEntry **pp, *p;
     241             : 
     242           0 :     hash = HASH(address);
     243           0 :     pp = hash_buckets + hash;
     244           0 :     while ((p = *pp) != 0) {
     245           0 :         if (p->address == address) goto gotit;
     246             : 
     247           0 :         pp = &p->next;
     248             :     }
     249             : 
     250             :     /* Expand the monitor cache if we have run out of free slots in the table */
     251           0 :     if (num_free_entries < FREE_THRESHOLD) {
     252             :         /* Expand monitor cache */
     253             : 
     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           0 :         if (!expanding) {
     259             :             PRStatus rv;
     260             : 
     261           0 :             expanding = PR_TRUE;
     262           0 :             rv = ExpandMonitorCache(num_hash_buckets_log2 + 1);
     263           0 :             expanding = PR_FALSE;
     264           0 :             if (PR_FAILURE == rv)  return NULL;
     265             : 
     266             :             /* redo the hash because it'll be different now */
     267           0 :             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           0 :             PR_ASSERT(num_free_entries > 0);
     274             :         }
     275             :     }
     276             : 
     277             :     /* Make a new monitor */
     278           0 :     p = free_entries;
     279           0 :     free_entries = p->next;
     280           0 :     num_free_entries--;
     281           0 :     if (OnMonitorRecycle && p->address)
     282           0 :         OnMonitorRecycle(p->address);
     283           0 :     p->address = address;
     284           0 :     p->next = hash_buckets[hash];
     285           0 :     hash_buckets[hash] = p;
     286           0 :     PR_ASSERT(p->cacheEntryCount == 0);
     287             : 
     288             :   gotit:
     289           0 :     p->cacheEntryCount++;
     290           0 :     return p->mon;
     291             : }
     292             : 
     293             : /*
     294             : ** Initialize the monitor cache
     295             : */
     296           3 : void _PR_InitCMon(void)
     297             : {
     298           3 :     _PR_NEW_LOCK_MCACHE();
     299           3 :     ExpandMonitorCache(3);
     300           3 : }
     301             : 
     302             : /*
     303             : ** Destroy the monitor cache
     304             : */
     305           0 : void _PR_CleanupCMon(void)
     306             : {
     307           0 :     _PR_DESTROY_LOCK_MCACHE();
     308             : 
     309           0 :     while (free_entries) {
     310           0 :         PR_DestroyMonitor(free_entries->mon);
     311           0 :         free_entries = free_entries->next;
     312             :     }
     313           0 :     num_free_entries = 0;
     314             : 
     315           0 :     while (mcache_blocks) {
     316             :         MonitorCacheEntryBlock *block;
     317             : 
     318           0 :         block = mcache_blocks;
     319           0 :         mcache_blocks = block->next;
     320           0 :         PR_DELETE(block);
     321             :     }
     322             : 
     323           0 :     PR_DELETE(hash_buckets);
     324           0 :     hash_mask = 0;
     325           0 :     num_hash_buckets = 0;
     326           0 :     num_hash_buckets_log2 = 0;
     327             : 
     328           0 :     expanding = PR_FALSE;
     329           0 :     OnMonitorRecycle = NULL;
     330           0 : }
     331             : 
     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;
     339             : 
     340           0 :     if (!_pr_initialized) _PR_ImplicitInitialization();
     341             : 
     342           0 :     _PR_LOCK_MCACHE();
     343           0 :     mon = CreateMonitor(address);
     344           0 :     _PR_UNLOCK_MCACHE();
     345             : 
     346           0 :     if (!mon) return NULL;
     347             : 
     348           0 :     PR_EnterMonitor(mon);
     349           0 :     return mon;
     350             : }
     351             : 
     352             : PR_IMPLEMENT(PRStatus) PR_CExitMonitor(void *address)
     353             : {
     354             :     MonitorCacheEntry **pp, *p;
     355           0 :     PRStatus status = PR_SUCCESS;
     356             : 
     357           0 :     _PR_LOCK_MCACHE();
     358           0 :     pp = LookupMonitorCacheEntry(address);
     359           0 :     if (pp != NULL) {
     360           0 :         p = *pp;
     361           0 :         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           0 :             p->address = 0;             /* defensive move */
     368           0 :             *pp = p->next;              /* unlink from hash_buckets */
     369           0 :             p->next = free_entries;     /* link into free list */
     370           0 :             free_entries = p;
     371           0 :             num_free_entries++;         /* count it as free */
     372             :         }
     373           0 :         status = PR_ExitMonitor(p->mon);
     374             :     } else {
     375           0 :         status = PR_FAILURE;
     376             :     }
     377           0 :     _PR_UNLOCK_MCACHE();
     378             : 
     379           0 :     return status;
     380             : }
     381             : 
     382             : PR_IMPLEMENT(PRStatus) PR_CWait(void *address, PRIntervalTime ticks)
     383             : {
     384             :     MonitorCacheEntry **pp;
     385             :     PRMonitor *mon;
     386             : 
     387           0 :     _PR_LOCK_MCACHE();
     388           0 :     pp = LookupMonitorCacheEntry(address);
     389           0 :     mon = pp ? ((*pp)->mon) : NULL;
     390           0 :     _PR_UNLOCK_MCACHE();
     391             : 
     392           0 :     if (mon == NULL)
     393           0 :         return PR_FAILURE;
     394           0 :     return PR_Wait(mon, ticks);
     395             : }
     396             : 
     397             : PR_IMPLEMENT(PRStatus) PR_CNotify(void *address)
     398             : {
     399             :     MonitorCacheEntry **pp;
     400             :     PRMonitor *mon;
     401             : 
     402           0 :     _PR_LOCK_MCACHE();
     403           0 :     pp = LookupMonitorCacheEntry(address);
     404           0 :     mon = pp ? ((*pp)->mon) : NULL;
     405           0 :     _PR_UNLOCK_MCACHE();
     406             : 
     407           0 :     if (mon == NULL)
     408           0 :         return PR_FAILURE;
     409           0 :     return PR_Notify(mon);
     410             : }
     411             : 
     412             : PR_IMPLEMENT(PRStatus) PR_CNotifyAll(void *address)
     413             : {
     414             :     MonitorCacheEntry **pp;
     415             :     PRMonitor *mon;
     416             : 
     417           0 :     _PR_LOCK_MCACHE();
     418           0 :     pp = LookupMonitorCacheEntry(address);
     419           0 :     mon = pp ? ((*pp)->mon) : NULL;
     420           0 :     _PR_UNLOCK_MCACHE();
     421             : 
     422           0 :     if (mon == NULL)
     423           0 :         return PR_FAILURE;
     424           0 :     return PR_NotifyAll(mon);
     425             : }
     426             : 
     427             : PR_IMPLEMENT(void)
     428             : PR_CSetOnMonitorRecycle(void (*callback)(void *address))
     429             : {
     430           0 :     OnMonitorRecycle = callback;
     431           0 : }

Generated by: LCOV version 1.13