/** * RemoveBinding : remove binding from collision detection on deactivation */ void nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding) { NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); if (!initialized) return; HashTableEntry * hashEntry; void * key = (void *)(uintptr_t)binding->mRecord.HashNumber(); hashEntry = (HashTableEntry*) PL_DHashTableSearch(&table, (void*)(uintptr_t) key); if (!hashEntry) { NS_WARNING("### disk cache: binding not in hashtable!"); return; } if (binding == hashEntry->mBinding) { if (PR_CLIST_IS_EMPTY(binding)) { // remove this hash entry PL_DHashTableRemove(&table, (void*)(uintptr_t) binding->mRecord.HashNumber()); return; } else { // promote next binding to head, and unlink this binding hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding); } } PR_REMOVE_AND_INIT_LINK(binding); }
/** * RemoveBinding : remove binding from collision detection on deactivation */ void nsDiskCacheBindery::RemoveBinding(nsDiskCacheBinding * binding) { NS_ASSERTION(initialized, "nsDiskCacheBindery not initialized"); if (!initialized) return; HashTableEntry * hashEntry; void * key = (void *)binding->mRecord.HashNumber(); hashEntry = (HashTableEntry*) PL_DHashTableOperate(&table, (void*) key, PL_DHASH_LOOKUP); if (!PL_DHASH_ENTRY_IS_BUSY(hashEntry)) { NS_WARNING("### disk cache: binding not in hashtable!"); return; } if (binding == hashEntry->mBinding) { if (PR_CLIST_IS_EMPTY(binding)) { // remove this hash entry (void) PL_DHashTableOperate(&table, (void*) binding->mRecord.HashNumber(), PL_DHASH_REMOVE); return; } else { // promote next binding to head, and unlink this binding hashEntry->mBinding = (nsDiskCacheBinding *)PR_NEXT_LINK(binding); } } PR_REMOVE_AND_INIT_LINK(binding); }
nsresult nsMemoryCacheDevice::OnDataSizeChange( nsCacheEntry * entry, int32_t deltaSize) { if (entry->IsStreamData()) { // we have the right to refuse or pre-evict uint32_t newSize = entry->DataSize() + deltaSize; if (EntryIsTooBig(newSize)) { #ifdef DEBUG nsresult rv = #endif nsCacheService::DoomEntry(entry); NS_ASSERTION(NS_SUCCEEDED(rv),"DoomEntry() failed."); return NS_ERROR_ABORT; } } // adjust our totals mTotalSize += deltaSize; if (!entry->IsDoomed()) { // move entry to the tail of the appropriate eviction list PR_REMOVE_AND_INIT_LINK(entry); PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, deltaSize)]); } EvictEntriesIfNecessary(); return NS_OK; }
nsresult nsMemoryCacheDevice::BindEntry(nsCacheEntry * entry) { if (!entry->IsDoomed()) { NS_ASSERTION(PR_CLIST_IS_EMPTY(entry),"entry is already on a list!"); // append entry to the eviction list PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]); // add entry to hashtable of mem cache entries nsresult rv = mMemCacheEntries.AddEntry(entry); if (NS_FAILED(rv)) { PR_REMOVE_AND_INIT_LINK(entry); return rv; } // add size of entry to memory totals ++mEntryCount; if (mMaxEntryCount < mEntryCount) mMaxEntryCount = mEntryCount; mTotalSize += entry->DataSize(); EvictEntriesIfNecessary(); } return NS_OK; }
PRBool nsCacheEntry::RemoveRequest(nsCacheRequest * request) { // XXX if debug: verify this request belongs to this entry PR_REMOVE_AND_INIT_LINK(request); // return true if this entry should stay active return !((PR_CLIST_IS_EMPTY(&mRequestQ)) && (PR_CLIST_IS_EMPTY(&mDescriptorQ))); }
/* * timer worker thread function */ static void timer_wstart(void *arg) { PRThreadPool *tp = (PRThreadPool *) arg; PRCList *qp; PRIntervalTime timeout; PRIntervalTime now; /* * call PR_WaitCondVar with minimum value of all timeouts */ while (!tp->shutdown) { PRJob *jobp; PR_Lock(tp->timerq.lock); if (PR_CLIST_IS_EMPTY(&tp->timerq.list)) { timeout = PR_INTERVAL_NO_TIMEOUT; } else { PRCList *qp; qp = tp->timerq.list.next; jobp = JOB_LINKS_PTR(qp); timeout = jobp->absolute - PR_IntervalNow(); if (timeout <= 0) timeout = PR_INTERVAL_NO_WAIT; /* already timed out */ } if (PR_INTERVAL_NO_WAIT != timeout) PR_WaitCondVar(tp->timerq.cv, timeout); if (tp->shutdown) { PR_Unlock(tp->timerq.lock); break; } /* * move expired-timer jobs to jobq */ now = PR_IntervalNow(); while (!PR_CLIST_IS_EMPTY(&tp->timerq.list)) { qp = tp->timerq.list.next; jobp = JOB_LINKS_PTR(qp); if ((PRInt32)(jobp->absolute - now) > 0) { break; } /* * job timed out */ PR_REMOVE_AND_INIT_LINK(&jobp->links); tp->timerq.cnt--; jobp->on_timerq = PR_FALSE; add_to_jobq(tp, jobp); } PR_Unlock(tp->timerq.lock); } }
/** * returns a string containing all the parameters in the ConfigStore hash set in the * format key1=value1&&key2=value2&& ... * The list will be lexically ordered by parameter key values. * The string needs to be freed by the caller. **/ TPS_PUBLIC const char* ConfigStore::GetOrderedList() { char *outstr = NULL; char *new_string = NULL; PRCList order_list; PR_INIT_CLIST(&order_list); PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &OrderLoop, &order_list); PR_Unlock(m_lock); PRCList *current = PR_LIST_HEAD(&order_list); PRCList *next; outstr = (char*) PR_Malloc(128); int allocated = 128; int needed = 0; PR_snprintf(outstr, 128, ""); while (current != &order_list) { OrderedEntry_t *entry = (OrderedEntry_t *) current; const char *value = GetConfigAsString(entry->key, ""); if ((entry != NULL) && (entry->key != NULL)) { needed = PL_strlen(outstr) + PL_strlen(entry->key) + PL_strlen(value) + 4; if (allocated <= needed) { while (allocated <= needed) { allocated = allocated * 2; } new_string = (char *)PR_Malloc(allocated); PR_snprintf(new_string, allocated, "%s", outstr); PR_Free(outstr); outstr = new_string; } PL_strcat(outstr, entry->key); PL_strcat(outstr, "="); PL_strcat(outstr, value); // free the memory for the Ordered Entry PL_strfree(entry->key); } next = PR_NEXT_LINK(current); PR_REMOVE_AND_INIT_LINK(current); if (current != NULL) { PR_Free(current); } current = next; if (current != &order_list) PL_strcat(outstr, "&&"); } return outstr; }
PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group) { PRRecvWait **desc; PRRecvWait *recv_wait = NULL; if (PR_FAILURE == MW_Init()) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return NULL; } if (NULL == group) group = mw_state->group; PR_ASSERT(NULL != group); if (NULL == group) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return NULL; } PR_Lock(group->ml); if (_prmw_stopped != group->state) { if (_prmw_running == group->state) group->state = _prmw_stopping; /* so nothing new comes in */ if (0 == group->waiting_threads) /* is there anybody else? */ group->state = _prmw_stopped; /* we can stop right now */ while (_prmw_stopped != group->state) (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT); /* make all the existing descriptors look done/interrupted */ for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc) { if (NULL != *desc) _MW_DoneInternal(group, desc, PR_MW_INTERRUPT); } PR_NotifyAllCondVar(group->new_business); } /* take first element of finished list and return it or NULL */ if (PR_CLIST_IS_EMPTY(&group->io_ready)) PR_SetError(PR_GROUP_EMPTY_ERROR, 0); else { PRCList *head = PR_LIST_HEAD(&group->io_ready); PR_REMOVE_AND_INIT_LINK(head); recv_wait = (PRRecvWait*)head; } PR_Unlock(group->ml); return recv_wait; } /* PR_CancelWaitGroup */
nsCacheEntry * nsMemoryCacheDevice::FindEntry(nsCString * key, bool *collision) { mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_MEMORY_SEARCH_2> timer; nsCacheEntry * entry = mMemCacheEntries.GetEntry(key); if (!entry) return nullptr; // move entry to the tail of an eviction list PR_REMOVE_AND_INIT_LINK(entry); PR_APPEND_LINK(entry, &mEvictionList[EvictionList(entry, 0)]); mInactiveSize -= entry->DataSize(); return entry; }
void nsCacheEntry::DetachDescriptors(void) { nsCacheEntryDescriptor * descriptor = (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ); while (descriptor != &mDescriptorQ) { nsCacheEntryDescriptor * nextDescriptor = (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor); descriptor->ClearCacheEntry(); PR_REMOVE_AND_INIT_LINK(descriptor); descriptor = nextDescriptor; } }
PRBool nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor) { NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!"); PR_REMOVE_AND_INIT_LINK(descriptor); descriptor->ClearCacheEntry(); if (!PR_CLIST_IS_EMPTY(&mDescriptorQ)) return PR_TRUE; // stay active if we still have open descriptors if (PR_CLIST_IS_EMPTY(&mRequestQ)) return PR_FALSE; // no descriptors or requests, we can deactivate return PR_TRUE; // find next best request to give a descriptor to }
void nsMemoryCacheDevice::EvictEntry(nsCacheEntry * entry, bool deleteEntry) { CACHE_LOG_DEBUG(("Evicting entry 0x%p from memory cache, deleting: %d\n", entry, deleteEntry)); // remove entry from our hashtable mMemCacheEntries.RemoveEntry(entry); // remove entry from the eviction list PR_REMOVE_AND_INIT_LINK(entry); // update statistics int32_t memoryRecovered = (int32_t)entry->DataSize(); mTotalSize -= memoryRecovered; if (!entry->IsDoomed()) mInactiveSize -= memoryRecovered; --mEntryCount; if (deleteEntry) delete entry; }
nsresult nsCacheEntry::CreateDescriptor(nsCacheRequest * request, nsCacheAccessMode accessGranted, nsICacheEntryDescriptor ** result) { NS_ENSURE_ARG_POINTER(request && result); nsCacheEntryDescriptor * descriptor = new nsCacheEntryDescriptor(this, accessGranted); // XXX check request is on q PR_REMOVE_AND_INIT_LINK(request); // remove request regardless of success if (descriptor == nsnull) return NS_ERROR_OUT_OF_MEMORY; PR_APPEND_LINK(descriptor, &mDescriptorQ); NS_ADDREF(*result = descriptor); return NS_OK; }
nsresult nsMemoryCacheDevice::Shutdown() { NS_ASSERTION(mInitialized, "### attempting shutdown while not initialized"); NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); mMemCacheEntries.Shutdown(); // evict all entries nsCacheEntry * entry, * next; for (int i = kQueueCount - 1; i >= 0; --i) { entry = (nsCacheEntry *)PR_LIST_HEAD(&mEvictionList[i]); while (entry != &mEvictionList[i]) { NS_ASSERTION(!entry->IsInUse(), "### shutting down with active entries"); next = (nsCacheEntry *)PR_NEXT_LINK(entry); PR_REMOVE_AND_INIT_LINK(entry); // update statistics int32_t memoryRecovered = (int32_t)entry->DataSize(); mTotalSize -= memoryRecovered; mInactiveSize -= memoryRecovered; --mEntryCount; delete entry; entry = next; } } /* * we're not factoring in changes to meta data yet... * NS_ASSERTION(mTotalSize == 0, "### mem cache leaking entries?"); */ NS_ASSERTION(mInactiveSize == 0, "### mem cache leaking entries?"); NS_ASSERTION(mEntryCount == 0, "### mem cache leaking entries?"); mInitialized = false; return NS_OK; }
void nsCacheEntry::DetachDescriptors(void) { nsCacheEntryDescriptor * descriptor = (nsCacheEntryDescriptor *)PR_LIST_HEAD(&mDescriptorQ); while (descriptor != &mDescriptorQ) { nsCacheEntryDescriptor * nextDescriptor = (nsCacheEntryDescriptor *)PR_NEXT_LINK(descriptor); // Doom entry if something bad happens while closing. See bug #673543 // Errors are handled different from RemoveDescriptor because this // method is only called from ClearDoomList (in which case the entry is // doomed anyway) and ClearActiveEntries (in which case we are shutting // down and really want to get rid of the entry immediately) if (NS_FAILED(descriptor->CloseOutput())) nsCacheService::DoomEntry(this); descriptor->ClearCacheEntry(); PR_REMOVE_AND_INIT_LINK(descriptor); descriptor = nextDescriptor; } }
bool nsCacheEntry::RemoveDescriptor(nsCacheEntryDescriptor * descriptor) { NS_ASSERTION(descriptor->CacheEntry() == this, "### Wrong cache entry!!"); nsresult rv = descriptor->CloseOutput(); if (rv == NS_BASE_STREAM_WOULD_BLOCK) return true; descriptor->ClearCacheEntry(); PR_REMOVE_AND_INIT_LINK(descriptor); // Doom entry if something bad happens while closing. See bug #673543 if (NS_FAILED(rv)) nsCacheService::DoomEntry(this); if (!PR_CLIST_IS_EMPTY(&mDescriptorQ)) return true; // stay active if we still have open descriptors if (PR_CLIST_IS_EMPTY(&mRequestQ)) return false; // no descriptors or requests, we can deactivate return true; // find next best request to give a descriptor to }
PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group) { PRRecvWait **desc; PRRecvWait *recv_wait = NULL; #ifdef WINNT _MDOverlapped *overlapped; PRRecvWait **end; PRThread *me = _PR_MD_CURRENT_THREAD(); #endif if (NULL == group) group = mw_state->group; PR_ASSERT(NULL != group); if (NULL == group) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); return NULL; } PR_Lock(group->ml); if (_prmw_stopped != group->state) { if (_prmw_running == group->state) group->state = _prmw_stopping; /* so nothing new comes in */ if (0 == group->waiting_threads) /* is there anybody else? */ group->state = _prmw_stopped; /* we can stop right now */ else { PR_NotifyAllCondVar(group->new_business); PR_NotifyAllCondVar(group->io_complete); } while (_prmw_stopped != group->state) (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT); } #ifdef WINNT _PR_MD_LOCK(&group->mdlock); #endif /* make all the existing descriptors look done/interrupted */ #ifdef WINNT end = &group->waiter->recv_wait + group->waiter->length; for (desc = &group->waiter->recv_wait; desc < end; ++desc) { if (NULL != *desc) { if (InterlockedCompareExchange((LONG *)&(*desc)->outcome, (LONG)PR_MW_INTERRUPT, (LONG)PR_MW_PENDING) == (LONG)PR_MW_PENDING) { PRFileDesc *bottom = PR_GetIdentitiesLayer( (*desc)->fd, PR_NSPR_IO_LAYER); PR_ASSERT(NULL != bottom); if (NULL == bottom) { PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); goto invalid_arg; } bottom->secret->state = _PR_FILEDESC_CLOSED; #if 0 fprintf(stderr, "cancel wait group: closing socket\n"); #endif if (closesocket(bottom->secret->md.osfd) == SOCKET_ERROR) { fprintf(stderr, "closesocket failed: %d\n", WSAGetLastError()); exit(1); } } } } while (group->waiter->count > 0) { _PR_THREAD_LOCK(me); me->state = _PR_IO_WAIT; PR_APPEND_LINK(&me->waitQLinks, &group->wait_list); if (!_PR_IS_NATIVE_THREAD(me)) { _PR_SLEEPQ_LOCK(me->cpu); _PR_ADD_SLEEPQ(me, PR_INTERVAL_NO_TIMEOUT); _PR_SLEEPQ_UNLOCK(me->cpu); } _PR_THREAD_UNLOCK(me); _PR_MD_UNLOCK(&group->mdlock); PR_Unlock(group->ml); _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); me->state = _PR_RUNNING; PR_Lock(group->ml); _PR_MD_LOCK(&group->mdlock); } #else for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc) { PR_ASSERT(desc < &group->waiter->recv_wait + group->waiter->length); if (NULL != *desc) _MW_DoneInternal(group, desc, PR_MW_INTERRUPT); } #endif /* take first element of finished list and return it or NULL */ if (PR_CLIST_IS_EMPTY(&group->io_ready)) PR_SetError(PR_GROUP_EMPTY_ERROR, 0); else { PRCList *head = PR_LIST_HEAD(&group->io_ready); PR_REMOVE_AND_INIT_LINK(head); #ifdef WINNT overlapped = (_MDOverlapped *) ((char *)head - offsetof(_MDOverlapped, data)); head = &overlapped->data.mw.desc->internal; if (NULL != overlapped->data.mw.timer) { PR_ASSERT(PR_INTERVAL_NO_TIMEOUT != overlapped->data.mw.desc->timeout); CancelTimer(overlapped->data.mw.timer); } else { PR_ASSERT(PR_INTERVAL_NO_TIMEOUT == overlapped->data.mw.desc->timeout); } PR_DELETE(overlapped); #endif recv_wait = (PRRecvWait*)head; } #ifdef WINNT invalid_arg: _PR_MD_UNLOCK(&group->mdlock); #endif PR_Unlock(group->ml); return recv_wait; } /* PR_CancelWaitGroup */
/** * Commits changes to the config file */ TPS_PUBLIC int ConfigStore::Commit(const bool backup, char *error_msg, int len) { char name_tmp[256], cdate[256], name_bak[256], bak_dir[256]; char basename[256], dirname[256]; PRFileDesc *ftmp = NULL; PRExplodedTime time; PRTime now; PRStatus status; if (m_cfg_file_path == NULL) { PR_snprintf(error_msg, len, "ConfigStore::Commit(): m_cfg_file_path is NULL!"); return 1; } if (strrchr(m_cfg_file_path, '/') != NULL) { PR_snprintf((char *) basename, 256, "%s", strrchr(m_cfg_file_path, '/') +1); PR_snprintf((char *) dirname, PL_strlen(m_cfg_file_path) - PL_strlen(basename), "%s", m_cfg_file_path); PL_strcat(dirname, '\0'); } else { PR_snprintf((char *) basename, 256, "%s", m_cfg_file_path); PR_snprintf((char *) dirname, 256, "."); } PR_snprintf(bak_dir, 256, "%s/bak", dirname); now = PR_Now(); PR_ExplodeTime(now, PR_LocalTimeParameters, &time); PR_snprintf(cdate, 16, "%04d%02d%02d%02d%02d%02dZ", time.tm_year, (time.tm_month + 1), time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); PR_snprintf(name_tmp, 256, "%s.%s.tmp", m_cfg_file_path,cdate); PR_snprintf(name_bak, 256, "%s/%s.%s", bak_dir, basename, cdate); ftmp = PR_Open(name_tmp, PR_WRONLY| PR_CREATE_FILE, 00400|00200); if (ftmp == NULL) { // unable to create temporary config file PR_snprintf(error_msg, len, "ConfigStore::Commit(): unable to create temporary config file"); return 1; } PRCList order_list; PR_INIT_CLIST(&order_list); PR_Lock(m_lock); PL_HashTableEnumerateEntries(m_root->getSet(), &OrderLoop, &order_list); PR_Unlock(m_lock); PRCList *current = PR_LIST_HEAD(&order_list); PRCList *next; while (current != &order_list) { OrderedEntry_t *entry = (OrderedEntry_t *) current; PR_Write(ftmp, entry->key, PL_strlen(entry->key)); PR_Write(ftmp, "=", 1); const char *value = GetConfigAsString(entry->key, ""); PR_Write(ftmp, value, PL_strlen(value)); PR_Write(ftmp, "\n", 1); // free the memory for the Ordered Entry if (entry->key != NULL) PL_strfree(entry->key); next = PR_NEXT_LINK(current); PR_REMOVE_AND_INIT_LINK(current); if (current != NULL) { PR_Free(current); } current = next; } PR_Close(ftmp); if (backup) { // create the backup directory if it does not exist if (PR_Access(bak_dir, PR_ACCESS_EXISTS) != PR_SUCCESS) { PR_MkDir(bak_dir, 00770); } status = PR_Rename(m_cfg_file_path, name_bak); if (status != PR_SUCCESS) { // failed to back up CS.cfg } } if (PR_Access(m_cfg_file_path, PR_ACCESS_EXISTS) == PR_SUCCESS) { // backup is false, or backup failed status = PR_Delete(m_cfg_file_path); if (status != PR_SUCCESS) { // failed to delete old CS.cfg file PR_snprintf(error_msg, len, "ConfigStore::Commit(): unable to delete old CS.cfg file"); return 1; } } status = PR_Rename(name_tmp, m_cfg_file_path); if (status != PR_SUCCESS) { // failed to move tmp to CS.cfg // major badness - we now have only tmp file, no CS.cfg PR_snprintf(error_msg, len, "ConfigStore::Commit(): failed to move tmp file to CS.cfg"); return 1; } return 0; }
PR_CancelJob(PRJob *jobp) { PRStatus rval = PR_FAILURE; PRThreadPool *tp; if (jobp->on_timerq) { /* * now, check again while holding the timerq lock */ tp = jobp->tpool; PR_Lock(tp->timerq.lock); if (jobp->on_timerq) { jobp->on_timerq = PR_FALSE; PR_REMOVE_AND_INIT_LINK(&jobp->links); tp->timerq.cnt--; PR_Unlock(tp->timerq.lock); if (!JOINABLE_JOB(jobp)) { delete_job(jobp); } else { JOIN_NOTIFY(jobp); } rval = PR_SUCCESS; } else PR_Unlock(tp->timerq.lock); } else if (jobp->on_ioq) { /* * now, check again while holding the ioq lock */ tp = jobp->tpool; PR_Lock(tp->ioq.lock); if (jobp->on_ioq) { jobp->cancel_cv = PR_NewCondVar(tp->ioq.lock); if (NULL == jobp->cancel_cv) { PR_Unlock(tp->ioq.lock); PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); return PR_FAILURE; } /* * mark job 'cancelled' and notify io thread(s) * XXXX: * this assumes there is only one io thread; when there * are multiple threads, the io thread processing this job * must be notified. */ jobp->cancel_io = PR_TRUE; PR_Unlock(tp->ioq.lock); /* release, reacquire ioq lock */ notify_ioq(tp); PR_Lock(tp->ioq.lock); while (jobp->cancel_io) PR_WaitCondVar(jobp->cancel_cv, PR_INTERVAL_NO_TIMEOUT); PR_Unlock(tp->ioq.lock); PR_ASSERT(!jobp->on_ioq); if (!JOINABLE_JOB(jobp)) { delete_job(jobp); } else { JOIN_NOTIFY(jobp); } rval = PR_SUCCESS; } else PR_Unlock(tp->ioq.lock); } if (PR_FAILURE == rval) PR_SetError(PR_INVALID_STATE_ERROR, 0); return rval; }
static NSFCStatus _NSFC_DeleteEntry(NSFCCache cache, NSFCEntryImpl *nep, PRBool hasBucketLock, PRBool delCntIt, PRBool outdCntIt) { NSFCStatus rfc = NSFC_OK; PR_ASSERT(nep); /* Get access to nep's bucket if caller doesn't have it already */ PRUint32 bucket; if (hasBucketLock == PR_FALSE) { if (nep->fDelete) { return rfc; } bucket = nep->hash % cache->hsize; NSFC_ACQUIREBUCKET(cache, bucket); } else { NSFC_ASSERTBUCKETHELD(cache, nep->hash % cache->hsize); } /* If nep hasn't already been marked for deletion... */ if (!nep->fDelete) { /* Remove nep from the hit list */ PR_Lock(cache->hitLock); PR_REMOVE_AND_INIT_LINK(&nep->hit_list); PR_Unlock(cache->hitLock); PR_ASSERT(PR_LIST_HEAD(&nep->hit_list) == PR_LIST_TAIL(&nep->hit_list)); /* Mark it for delete */ nep->fDelete = 1; /* Increment total count of entries deleted */ if (delCntIt == PR_TRUE) { PR_AtomicIncrement((PRInt32 *)&cache->delCnt); PR_AtomicIncrement((PRInt32 *)&cache->busydCnt); if (outdCntIt == PR_TRUE) PR_AtomicIncrement((PRInt32 *)&cache->outdCnt); } } if (nep->refcnt == 0) { /* * Given that the entry's use count is zero, the caller had better have * the bucket lock (because he clearly doesn't have a refcnt) */ PR_ASSERT(hasBucketLock == PR_TRUE); _NSFC_DestroyEntry(cache, nep, delCntIt); } /* Release the bucket lock if we weren't holding it on entry */ if (hasBucketLock == PR_FALSE) { NSFC_RELEASEBUCKET(cache, bucket); } /* Cache contents have been modified */ cache->sig++; return rfc; }
/* * io worker thread function */ static void io_wstart(void *arg) { PRThreadPool *tp = (PRThreadPool *) arg; int pollfd_cnt, pollfds_used; int rv; PRCList *qp, *nextqp; PRPollDesc *pollfds; PRJob **polljobs; int poll_timeout; PRIntervalTime now; /* * scan io_jobq * construct poll list * call PR_Poll * for all fds, for which poll returns true, move the job to * jobq and wakeup worker thread. */ while (!tp->shutdown) { PRJob *jobp; pollfd_cnt = tp->ioq.cnt + 10; if (pollfd_cnt > tp->ioq.npollfds) { /* * re-allocate pollfd array if the current one is not large * enough */ if (NULL != tp->ioq.pollfds) PR_Free(tp->ioq.pollfds); tp->ioq.pollfds = (PRPollDesc *) PR_Malloc(pollfd_cnt * (sizeof(PRPollDesc) + sizeof(PRJob *))); PR_ASSERT(NULL != tp->ioq.pollfds); /* * array of pollfds */ pollfds = tp->ioq.pollfds; tp->ioq.polljobs = (PRJob **) (&tp->ioq.pollfds[pollfd_cnt]); /* * parallel array of jobs */ polljobs = tp->ioq.polljobs; tp->ioq.npollfds = pollfd_cnt; } pollfds_used = 0; /* * add the notify fd; used for unblocking io thread(s) */ pollfds[pollfds_used].fd = tp->ioq.notify_fd; pollfds[pollfds_used].in_flags = PR_POLL_READ; pollfds[pollfds_used].out_flags = 0; polljobs[pollfds_used] = NULL; pollfds_used++; /* * fill in the pollfd array */ PR_Lock(tp->ioq.lock); for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) { nextqp = qp->next; jobp = JOB_LINKS_PTR(qp); if (jobp->cancel_io) { CANCEL_IO_JOB(jobp); continue; } if (pollfds_used == (pollfd_cnt)) break; pollfds[pollfds_used].fd = jobp->iod->socket; pollfds[pollfds_used].in_flags = jobp->io_poll_flags; pollfds[pollfds_used].out_flags = 0; polljobs[pollfds_used] = jobp; pollfds_used++; } if (!PR_CLIST_IS_EMPTY(&tp->ioq.list)) { qp = tp->ioq.list.next; jobp = JOB_LINKS_PTR(qp); if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) poll_timeout = PR_INTERVAL_NO_TIMEOUT; else if (PR_INTERVAL_NO_WAIT == jobp->timeout) poll_timeout = PR_INTERVAL_NO_WAIT; else { poll_timeout = jobp->absolute - PR_IntervalNow(); if (poll_timeout <= 0) /* already timed out */ poll_timeout = PR_INTERVAL_NO_WAIT; } } else { poll_timeout = PR_INTERVAL_NO_TIMEOUT; } PR_Unlock(tp->ioq.lock); /* * XXXX * should retry if more jobs have been added to the queue? * */ PR_ASSERT(pollfds_used <= pollfd_cnt); rv = PR_Poll(tp->ioq.pollfds, pollfds_used, poll_timeout); if (tp->shutdown) { break; } if (rv > 0) { /* * at least one io event is set */ PRStatus rval_status; PRInt32 index; PR_ASSERT(pollfds[0].fd == tp->ioq.notify_fd); /* * reset the pollable event, if notified */ if (pollfds[0].out_flags & PR_POLL_READ) { rval_status = PR_WaitForPollableEvent(tp->ioq.notify_fd); PR_ASSERT(PR_SUCCESS == rval_status); } for(index = 1; index < (pollfds_used); index++) { PRInt16 events = pollfds[index].in_flags; PRInt16 revents = pollfds[index].out_flags; jobp = polljobs[index]; if ((revents & PR_POLL_NVAL) || /* busted in all cases */ (revents & PR_POLL_ERR) || ((events & PR_POLL_WRITE) && (revents & PR_POLL_HUP))) { /* write op & hup */ PR_Lock(tp->ioq.lock); if (jobp->cancel_io) { CANCEL_IO_JOB(jobp); PR_Unlock(tp->ioq.lock); continue; } PR_REMOVE_AND_INIT_LINK(&jobp->links); tp->ioq.cnt--; jobp->on_ioq = PR_FALSE; PR_Unlock(tp->ioq.lock); /* set error */ if (PR_POLL_NVAL & revents) jobp->iod->error = PR_BAD_DESCRIPTOR_ERROR; else if (PR_POLL_HUP & revents) jobp->iod->error = PR_CONNECT_RESET_ERROR; else jobp->iod->error = PR_IO_ERROR; /* * add to jobq */ add_to_jobq(tp, jobp); } else if (revents) { /* * add to jobq */ PR_Lock(tp->ioq.lock); if (jobp->cancel_io) { CANCEL_IO_JOB(jobp); PR_Unlock(tp->ioq.lock); continue; } PR_REMOVE_AND_INIT_LINK(&jobp->links); tp->ioq.cnt--; jobp->on_ioq = PR_FALSE; PR_Unlock(tp->ioq.lock); if (jobp->io_op == JOB_IO_CONNECT) { if (PR_GetConnectStatus(&pollfds[index]) == PR_SUCCESS) jobp->iod->error = 0; else jobp->iod->error = PR_GetError(); } else jobp->iod->error = 0; add_to_jobq(tp, jobp); } } } /* * timeout processing */ now = PR_IntervalNow(); PR_Lock(tp->ioq.lock); for (qp = tp->ioq.list.next; qp != &tp->ioq.list; qp = nextqp) { nextqp = qp->next; jobp = JOB_LINKS_PTR(qp); if (jobp->cancel_io) { CANCEL_IO_JOB(jobp); continue; } if (PR_INTERVAL_NO_TIMEOUT == jobp->timeout) break; if ((PR_INTERVAL_NO_WAIT != jobp->timeout) && ((PRInt32)(jobp->absolute - now) > 0)) break; PR_REMOVE_AND_INIT_LINK(&jobp->links); tp->ioq.cnt--; jobp->on_ioq = PR_FALSE; jobp->iod->error = PR_IO_TIMEOUT_ERROR; add_to_jobq(tp, jobp); } PR_Unlock(tp->ioq.lock); } }
/* * worker thread function */ static void wstart(void *arg) { PRThreadPool *tp = (PRThreadPool *) arg; PRCList *head; /* * execute jobs until shutdown */ while (!tp->shutdown) { PRJob *jobp; #ifdef OPT_WINNT BOOL rv; DWORD unused, shutdown; LPOVERLAPPED olp; PR_Lock(tp->jobq.lock); tp->idle_threads++; PR_Unlock(tp->jobq.lock); rv = GetQueuedCompletionStatus(tp->jobq.nt_completion_port, &unused, &shutdown, &olp, INFINITE); PR_ASSERT(rv); if (shutdown) break; jobp = ((NT_notifier *) olp)->jobp; PR_Lock(tp->jobq.lock); tp->idle_threads--; tp->jobq.cnt--; PR_Unlock(tp->jobq.lock); #else PR_Lock(tp->jobq.lock); while (PR_CLIST_IS_EMPTY(&tp->jobq.list) && (!tp->shutdown)) { tp->idle_threads++; PR_WaitCondVar(tp->jobq.cv, PR_INTERVAL_NO_TIMEOUT); tp->idle_threads--; } if (tp->shutdown) { PR_Unlock(tp->jobq.lock); break; } head = PR_LIST_HEAD(&tp->jobq.list); /* * remove job from queue */ PR_REMOVE_AND_INIT_LINK(head); tp->jobq.cnt--; jobp = JOB_LINKS_PTR(head); PR_Unlock(tp->jobq.lock); #endif jobp->job_func(jobp->job_arg); if (!JOINABLE_JOB(jobp)) { delete_job(jobp); } else { JOIN_NOTIFY(jobp); } } PR_Lock(tp->jobq.lock); tp->current_threads--; PR_Unlock(tp->jobq.lock); }
PR_JoinThreadPool(PRThreadPool *tpool) { PRStatus rval = PR_SUCCESS; PRCList *head; PRStatus rval_status; PR_Lock(tpool->jobq.lock); while (!tpool->shutdown) PR_WaitCondVar(tpool->shutdown_cv, PR_INTERVAL_NO_TIMEOUT); /* * wakeup worker threads */ #ifdef OPT_WINNT /* * post shutdown notification for all threads */ { int i; for(i=0; i < tpool->current_threads; i++) { PostQueuedCompletionStatus(tpool->jobq.nt_completion_port, 0, TRUE, NULL); } } #else PR_NotifyAllCondVar(tpool->jobq.cv); #endif /* * wakeup io thread(s) */ notify_ioq(tpool); /* * wakeup timer thread(s) */ PR_Lock(tpool->timerq.lock); notify_timerq(tpool); PR_Unlock(tpool->timerq.lock); while (!PR_CLIST_IS_EMPTY(&tpool->jobq.wthreads)) { wthread *wthrp; head = PR_LIST_HEAD(&tpool->jobq.wthreads); PR_REMOVE_AND_INIT_LINK(head); PR_Unlock(tpool->jobq.lock); wthrp = WTHREAD_LINKS_PTR(head); rval_status = PR_JoinThread(wthrp->thread); PR_ASSERT(PR_SUCCESS == rval_status); PR_DELETE(wthrp); PR_Lock(tpool->jobq.lock); } PR_Unlock(tpool->jobq.lock); while (!PR_CLIST_IS_EMPTY(&tpool->ioq.wthreads)) { wthread *wthrp; head = PR_LIST_HEAD(&tpool->ioq.wthreads); PR_REMOVE_AND_INIT_LINK(head); wthrp = WTHREAD_LINKS_PTR(head); rval_status = PR_JoinThread(wthrp->thread); PR_ASSERT(PR_SUCCESS == rval_status); PR_DELETE(wthrp); } while (!PR_CLIST_IS_EMPTY(&tpool->timerq.wthreads)) { wthread *wthrp; head = PR_LIST_HEAD(&tpool->timerq.wthreads); PR_REMOVE_AND_INIT_LINK(head); wthrp = WTHREAD_LINKS_PTR(head); rval_status = PR_JoinThread(wthrp->thread); PR_ASSERT(PR_SUCCESS == rval_status); PR_DELETE(wthrp); } /* * Delete queued jobs */ while (!PR_CLIST_IS_EMPTY(&tpool->jobq.list)) { PRJob *jobp; head = PR_LIST_HEAD(&tpool->jobq.list); PR_REMOVE_AND_INIT_LINK(head); jobp = JOB_LINKS_PTR(head); tpool->jobq.cnt--; delete_job(jobp); } /* delete io jobs */ while (!PR_CLIST_IS_EMPTY(&tpool->ioq.list)) { PRJob *jobp; head = PR_LIST_HEAD(&tpool->ioq.list); PR_REMOVE_AND_INIT_LINK(head); tpool->ioq.cnt--; jobp = JOB_LINKS_PTR(head); delete_job(jobp); } /* delete timer jobs */ while (!PR_CLIST_IS_EMPTY(&tpool->timerq.list)) { PRJob *jobp; head = PR_LIST_HEAD(&tpool->timerq.list); PR_REMOVE_AND_INIT_LINK(head); tpool->timerq.cnt--; jobp = JOB_LINKS_PTR(head); delete_job(jobp); } PR_ASSERT(0 == tpool->jobq.cnt); PR_ASSERT(0 == tpool->ioq.cnt); PR_ASSERT(0 == tpool->timerq.cnt); delete_threadpool(tpool); return rval; }
static void PR_CALLBACK Server(void *arg) { PRStatus rv; PRNetAddr serverAddress; PRThread *me = PR_GetCurrentThread(); CSServer_t *server = (CSServer_t*)arg; PRSocketOptionData sockOpt; server->listener = PR_Socket(domain, SOCK_STREAM, protocol); sockOpt.option = PR_SockOpt_Reuseaddr; sockOpt.value.reuse_addr = PR_TRUE; rv = PR_SetSocketOption(server->listener, &sockOpt); TEST_ASSERT(PR_SUCCESS == rv); memset(&serverAddress, 0, sizeof(serverAddress)); if (PR_AF_INET6 != domain) rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress); else rv = PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, DEFAULT_PORT, &serverAddress); rv = PR_Bind(server->listener, &serverAddress); TEST_ASSERT(PR_SUCCESS == rv); rv = PR_Listen(server->listener, server->backlog); TEST_ASSERT(PR_SUCCESS == rv); server->started = PR_IntervalNow(); TimeOfDayMessage("Server started at", me); PR_Lock(server->ml); server->state = cs_run; PR_NotifyCondVar(server->stateChange); PR_Unlock(server->ml); /* ** Create the first worker (actually, a thread that accepts ** connections and then processes the work load as needed). ** From this point on, additional worker threads are created ** as they are needed by existing worker threads. */ rv = CreateWorker(server, &server->pool); TEST_ASSERT(PR_SUCCESS == rv); /* ** From here on this thread is merely hanging around as the contact ** point for the main test driver. It's just waiting for the driver ** to declare the test complete. */ TEST_LOG( cltsrv_log_file, TEST_LOG_VERBOSE, ("\tServer(0x%p): waiting for state change\n", me)); PR_Lock(server->ml); while ((cs_run == server->state) && !Aborted(rv)) { rv = PR_WaitCondVar(server->stateChange, PR_INTERVAL_NO_TIMEOUT); } PR_Unlock(server->ml); PR_ClearInterrupt(); TEST_LOG( cltsrv_log_file, TEST_LOG_INFO, ("\tServer(0x%p): shutting down workers\n", me)); /* ** Get all the worker threads to exit. They know how to ** clean up after themselves, so this is just a matter of ** waiting for clorine in the pool to take effect. During ** this stage we're ignoring interrupts. */ server->workers.minimum = server->workers.maximum = 0; PR_Lock(server->ml); while (!PR_CLIST_IS_EMPTY(&server->list)) { PRCList *head = PR_LIST_HEAD(&server->list); CSWorker_t *worker = (CSWorker_t*)head; TEST_LOG( cltsrv_log_file, TEST_LOG_VERBOSE, ("\tServer(0x%p): interrupting worker(0x%p)\n", me, worker)); rv = PR_Interrupt(worker->thread); TEST_ASSERT(PR_SUCCESS == rv); PR_REMOVE_AND_INIT_LINK(head); } while (server->pool.workers > 0) { TEST_LOG( cltsrv_log_file, TEST_LOG_NOTICE, ("\tServer(0x%p): waiting for %u workers to exit\n", me, server->pool.workers)); (void)PR_WaitCondVar(server->pool.exiting, PR_INTERVAL_NO_TIMEOUT); } server->state = cs_exit; PR_NotifyCondVar(server->stateChange); PR_Unlock(server->ml); TEST_LOG( cltsrv_log_file, TEST_LOG_ALWAYS, ("\tServer(0x%p): stopped after %u operations and %u bytes\n", me, server->operations, server->bytesTransferred)); if (NULL != server->listener) PR_Close(server->listener); server->stopped = PR_IntervalNow(); } /* Server */
static void PR_CALLBACK Worker(void *arg) { PRStatus rv; PRNetAddr from; PRFileDesc *fd = NULL; PRThread *me = PR_GetCurrentThread(); CSWorker_t *worker = (CSWorker_t*)arg; CSServer_t *server = worker->server; CSPool_t *pool = &server->pool; TEST_LOG( cltsrv_log_file, TEST_LOG_NOTICE, ("\t\tWorker(0x%p): started [%u]\n", me, pool->workers + 1)); PR_Lock(server->ml); PR_APPEND_LINK(&worker->element, &server->list); pool->workers += 1; /* define our existance */ while (cs_run == server->state) { while (pool->accepting >= server->workers.accepting) { TEST_LOG( cltsrv_log_file, TEST_LOG_VERBOSE, ("\t\tWorker(0x%p): waiting for accept slot[%d]\n", me, pool->accepting)); rv = PR_WaitCondVar(pool->acceptComplete, PR_INTERVAL_NO_TIMEOUT); if (Aborted(rv) || (cs_run != server->state)) { TEST_LOG( cltsrv_log_file, TEST_LOG_NOTICE, ("\tWorker(0x%p): has been %s\n", me, (Aborted(rv) ? "interrupted" : "stopped"))); goto exit; } } pool->accepting += 1; /* how many are really in accept */ PR_Unlock(server->ml); TEST_LOG( cltsrv_log_file, TEST_LOG_VERBOSE, ("\t\tWorker(0x%p): calling accept\n", me)); fd = PR_Accept(server->listener, &from, PR_INTERVAL_NO_TIMEOUT); PR_Lock(server->ml); pool->accepting -= 1; PR_NotifyCondVar(pool->acceptComplete); if ((NULL == fd) && Aborted(PR_FAILURE)) { if (NULL != server->listener) { PR_Close(server->listener); server->listener = NULL; } goto exit; } if (NULL != fd) { /* ** Create another worker of the total number of workers is ** less than the minimum specified or we have none left in ** accept() AND we're not over the maximum. ** This sort of presumes that the number allowed in accept ** is at least as many as the minimum. Otherwise we'll keep ** creating new threads and deleting them soon after. */ PRBool another = ((pool->workers < server->workers.minimum) || ((0 == pool->accepting) && (pool->workers < server->workers.maximum))) ? PR_TRUE : PR_FALSE; pool->active += 1; PR_Unlock(server->ml); if (another) (void)CreateWorker(server, pool); rv = ProcessRequest(fd, server); if (PR_SUCCESS != rv) TEST_LOG( cltsrv_log_file, TEST_LOG_ERROR, ("\t\tWorker(0x%p): server process ended abnormally\n", me)); (void)PR_Close(fd); fd = NULL; PR_Lock(server->ml); pool->active -= 1; } } exit: PR_ClearInterrupt(); PR_Unlock(server->ml); if (NULL != fd) { (void)PR_Shutdown(fd, PR_SHUTDOWN_BOTH); (void)PR_Close(fd); } TEST_LOG( cltsrv_log_file, TEST_LOG_NOTICE, ("\t\tWorker(0x%p): exiting [%u]\n", PR_GetCurrentThread(), pool->workers)); PR_Lock(server->ml); pool->workers -= 1; /* undefine our existance */ PR_REMOVE_AND_INIT_LINK(&worker->element); PR_NotifyCondVar(pool->exiting); PR_Unlock(server->ml); PR_DELETE(worker); /* destruction of the "worker" object */ } /* Worker */
SECStatus do_accepts( PRFileDesc *listen_sock, PRFileDesc *model_sock, int requestCert ) { PRNetAddr addr; PRErrorCode perr; #ifdef XP_UNIX struct sigaction act; #endif VLOG(("selfserv: do_accepts: starting")); PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH); acceptorThread = PR_GetCurrentThread(); #ifdef XP_UNIX /* set up the signal handler */ act.sa_handler = sigusr1_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(SIGUSR1, &act, NULL)) { fprintf(stderr, "Error installing signal handler.\n"); exit(1); } #endif while (!stopping) { PRFileDesc *tcp_sock; PRCList *myLink; FPRINTF(stderr, "\n\n\nselfserv: About to call accept.\n"); tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT); if (tcp_sock == NULL) { perr = PR_GetError(); if ((perr != PR_CONNECT_RESET_ERROR && perr != PR_PENDING_INTERRUPT_ERROR) || verbose) { errWarn("PR_Accept"); } if (perr == PR_CONNECT_RESET_ERROR) { FPRINTF(stderr, "Ignoring PR_CONNECT_RESET_ERROR error - continue\n"); continue; } stopping = 1; break; } VLOG(("selfserv: do_accept: Got connection\n")); PZ_Lock(qLock); while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) { PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT); } if (stopping) { PZ_Unlock(qLock); if (tcp_sock) { PR_Close(tcp_sock); } break; } myLink = PR_LIST_HEAD(&freeJobs); PR_REMOVE_AND_INIT_LINK(myLink); /* could release qLock here and reacquire it 7 lines below, but ** why bother for 4 assignment statements? */ { JOB * myJob = (JOB *)myLink; myJob->tcp_sock = tcp_sock; myJob->model_sock = model_sock; myJob->requestCert = requestCert; } PR_APPEND_LINK(myLink, &jobQ); PZ_NotifyCondVar(jobQNotEmptyCv); PZ_Unlock(qLock); } FPRINTF(stderr, "selfserv: Closing listen socket.\n"); VLOG(("selfserv: do_accepts: exiting")); if (listen_sock) { PR_Close(listen_sock); } return SECSuccess; }
void nsIOThreadPool::ThreadFunc(void *arg) { nsIOThreadPool *pool = (nsIOThreadPool *) arg; LOG(("entering ThreadFunc\n")); { nsAutoLock lock(pool->mLock); for (;;) { PRIntervalTime start = PR_IntervalNow(), timeout = IDLE_TIMEOUT; // // wait for one or more of the following to occur: // (1) the event queue has an event to process // (2) the shutdown flag has been set // (3) the thread has been idle for too long // // PR_WaitCondVar will return when any of these conditions is true. // while (PR_CLIST_IS_EMPTY(&pool->mEventQ) && !pool->mShutdown) { pool->mNumIdleThreads++; PR_WaitCondVar(pool->mIdleThreadCV, timeout); pool->mNumIdleThreads--; PRIntervalTime delta = PR_IntervalNow() - start; if (delta >= timeout) break; timeout -= delta; start += delta; } // if the queue is still empty, then kill this thread (either we // are shutting down or the thread exceeded the idle timeout)... if (PR_CLIST_IS_EMPTY(&pool->mEventQ)) break; // handle one event at a time: we don't want this one thread to hog // all the events while other threads may be able to help out ;-) do { PLEvent *event = PLEVENT_FROM_LINK(PR_LIST_HEAD(&pool->mEventQ)); PR_REMOVE_AND_INIT_LINK(&event->link); LOG(("event:%p\n", event)); // release lock! lock.unlock(); PL_HandleEvent(event); lock.lock(); } while (!PR_CLIST_IS_EMPTY(&pool->mEventQ)); } // thread is going away... pool->mNumThreads--; PR_NotifyCondVar(pool->mExitThreadCV); } // release our reference to the pool NS_RELEASE(pool); LOG(("leaving ThreadFunc\n")); }