home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / threads / prcmon.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  10.6 KB  |  393 lines

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