reqpool.cpp revision c999f225d03074008a0c21cdd5d3594da476e243
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * IPRT - Request Pool.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Copyright (C) 2006-2011 Oracle Corporation
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * available from http://www.virtualbox.org. This file is free software;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * you can redistribute it and/or modify it under the terms of the GNU
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * General Public License (GPL) as published by the Free Software
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * The contents of this file may alternatively be used under the terms
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * of the Common Development and Distribution License Version 1.0
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * VirtualBox OSE distribution, in which case the provisions of the
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * CDDL are applicable instead of those of the GPL.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * You may elect to license modified versions of this file under the
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * terms and conditions of either the GPL or the CDDL or both.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync/*******************************************************************************
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync* Header Files *
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync*******************************************************************************/
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync/*******************************************************************************
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync* Structures and Typedefs *
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync*******************************************************************************/
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Node in the RTREQPOOLINT::IdleThreads list. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Node in the RTREQPOOLINT::WorkerThreads list. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The submit timestamp of the pending request. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The submit timestamp of the request processing. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** When this CPU went idle the last time. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The number of requests processed by this thread. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Total time the requests processed by this thread took to process. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Total time the requests processed by this thread had to wait in
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * the queue before being scheduled. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The CPU this was scheduled last time we checked. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The submitter will put an incoming request here when scheduling an idle
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * thread. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The request the thread is currently processing. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The thread handle. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Nano seconds timestamp representing the birth time of the thread. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Pointer to the request thread pool instance the thread is associated
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync/** Pointer to a worker thread. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Request thread pool instance data.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsynctypedef struct RTREQPOOLINT
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Magic value (RTREQPOOL_MAGIC). */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The worker thread type. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The current number of worker threads. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The maximum number of worker threads. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The number of threads which should be spawned before throttling kicks
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The minimum number of worker threads. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The number of milliseconds a thread needs to be idle before it is
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * considered for retirement. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The max number of milliseconds to push back a submitter before creating
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * a new worker thread once the threshold has been reached. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The minimum number of milliseconds to push back a submitter before
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * creating a new worker thread once the threshold has been reached. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The current submitter push back in milliseconds.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * This is recalculated when worker threads come and go. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Statistics: The total number of threads created. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Statistics: The timestamp when the last thread was created. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Linked list of worker threads. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Event semaphore that submitters block on when pushing back . */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Critical section serializing access to members of this structure. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Destruction indicator. The worker threads checks in their loop. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync bool volatile fDestructing;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Reference counter. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Number of threads pushing back. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** The number of idle thread or threads in the process of becoming
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * idle. This is increased before the to-be-idle thread tries to enter
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * the critical section and add itself to the list. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Linked list of idle threads. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Head of the request FIFO. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** Where to insert the next request. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync/** Pointer to a request thread pool instance. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsyncstatic void rtReqPoolRecalcPushBack(PRTREQPOOLINT pPool)
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync uint32_t const cMsRange = pPool->cMsMaxPushBack - pPool->cMsMinPushBack;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync uint32_t const cSteps = pPool->cMaxThreads - pPool->cThreadsThreshold;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync uint32_t const iStep = pPool->cCurThreads - pPool->cThreadsThreshold;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync cMsCurPushBack = (uint32_t)( (uint64_t)cMsRange * RT_NS_1MS / cSteps * iStep / RT_NS_1MS );
316572fd6bf59ec1038f0476f6536fc10163beebvboxsyncstatic void rtReqPoolThreadProcessRequest(PRTREQPOOLTHREAD pThread, PRTREQINT pReq)
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Update thread state.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Do the actual processing.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** @todo */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Update thread statistics and state.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pThread->cNsTotalReqProcessing += uNsTsEnd - pThread->uProcessingNanoTs;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pThread->cNsTotalReqQueued += uNsTsEnd - pThread->uPendingNanoTs;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsyncstatic DECLCALLBACK(int) rtReqPoolThreadProc(RTTHREAD hThreadSelf, void *pvArg)
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync PRTREQPOOLTHREAD pThread = (PRTREQPOOLTHREAD)pvArg;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * The work loop.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Pending work?
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync PRTREQINT pReq = ASMAtomicXchgPtrT(&pThread->pTodoReq, NULL, PRTREQINT);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /* Recheck the todo request pointer after entering the critsect. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pReq = ASMAtomicXchgPtrT(&pThread->pTodoReq, NULL, PRTREQINT);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /* Any pending requests in the queue? */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pPool->ppPendingRequests = &pPool->pPendingRequests;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Un-idle ourselves and process the request.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Nothing to do, go idle.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync RTListPrepend(&pPool->IdleThreads, &pThread->IdleNode);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Clean up on the way out.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** @todo .... */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsyncstatic void rtReqPoolCreateNewWorker(RTREQPOOL pPool)
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync PRTREQPOOLTHREAD pThread = (PRTREQPOOLTHREAD)RTMemAllocZ(sizeof(RTREQPOOLTHREAD));
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync RTListAppend(&pPool->WorkerThreads, &pThread->ListNode);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync int rc = RTThreadCreateF(&pThread->hThread, rtReqPoolThreadProc, pThread, 0 /*default stack size*/,
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pPool->enmThreadType, RTTHREADFLAGS_WAITABLE, "REQPT%02u", ++s_idThread);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pPool->uLastThreadCreateNanoTs = pThread->uBirthNanoTs;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsyncDECLHIDDEN(int) rtReqPoolSubmit(PRTREQPOOLINT pPool, PRTREQINT pReq)
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Prepare the request.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Try schedule the request to any currently idle thread.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync PRTREQPOOLTHREAD pThread = RTListGetFirst(&pPool->IdleThreads, RTREQPOOLTHREAD, IdleNode);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** @todo CPU affinity... */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Put the request in the pending queue.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync pPool->ppPendingRequests = (PRTREQINT*)&pReq->pNext;
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * If there is an incoming worker thread already or we've reached the
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * maximum number of worker threads, we're done.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Push back before creating a new worker thread.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync && (RTTimeNanoTS() - pReq->uSubmitNanoTs) / RT_NS_1MS >= pPool->cMsCurPushBack )
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** @todo this is everything but perfect... it makes wake up order
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * assumptions. A better solution would be having a lazily
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * allocated push back event on each request. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync int rc = RTSemEventWait(pPool->hPushBackEvt, cMsTimeout);
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync /** @todo check if it's still on the list before going on. */
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * Create a new thread for processing the request.
316572fd6bf59ec1038f0476f6536fc10163beebvboxsync * For simplicity, we don't bother leaving the critical section while doing so.