HGCMThread.cpp revision efd2d6729030f314cbab2ceca59903ba80697246
/* $Id$ */
/** @file
* HGCMThread - Host-Guest Communication Manager Threads
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "Logging.h"
#include "HGCMThread.h"
#include <iprt/semaphore.h>
/* HGCM uses worker threads, which process messages from other threads.
* A message consists of the message header and message specific data.
* Message header is opaque for callers, but message data is defined
* and used by them.
*
* Messages are distinguished by message identifier and worker thread
* they are allocated for.
*
* Messages are allocated for a worker thread and belong to
* the thread. A worker thread holds the queue of messages.
*
* The calling thread creates a message, specifying which worker thread
* the message is created for, then, optionally, initializes message
* specific data and, also optionally, references the message.
*
* Message then is posted or sent to worker thread by inserting
* it to the worker thread message queue and referencing the message.
* Worker thread then again may fetch next message.
*
* Upon processing the message the worker thread dereferences it.
* Dereferencing also automatically deletes message from the thread
* queue and frees memory allocated for the message, if no more
* references left. If there are references, the message remains
* in the queue.
*
*/
/* Version of HGCM message header */
#define HGCMMSG_VERSION (1)
/* Thread is initializing. */
#define HGCMMSG_TF_INITIALIZING (0x00000001)
/* Thread must be terminated. */
#define HGCMMSG_TF_TERMINATE (0x00000002)
/* Thread has been terminated. */
#define HGCMMSG_TF_TERMINATED (0x00000004)
/** @todo consider use of RTReq */
class HGCMThread: public HGCMObject
{
private:
/* Worker thread function. */
/* A user supplied thread parameter. */
void *m_pvUser;
/* The thread runtime handle. */
/* Event the thread waits for, signalled when a message
* to process is posted to the thread.
*/
/* A caller thread waits for completion of a SENT message on this event. */
int32_t volatile m_i32MessagesProcessed;
/* Critical section for accessing the thread data, mostly for message queues. */
/* Message queue variables. Messages are inserted at tail of message
* queue. They are consumed by worker thread sequentially. If a message was
* consumed, it is removed from message queue.
*/
/* Head of message queue. */
/* Message which another message will be inserted after. */
/* Head of messages being processed queue. */
/* Message which another message will be inserted after. */
/* Head of free message structures list. */
/* Tail of free message structures list. */
inline int Enter (void);
inline void Leave (void);
HGCMMsgCore *FetchFreeListHead (void);
protected:
virtual ~HGCMThread (void);
public:
HGCMThread ();
int WaitForTermination (void);
int Initialize (HGCMTHREADHANDLE handle, const char *pszThreadName, PFNHGCMTHREAD pfnThread, void *pvUser);
};
/*
* HGCMMsgCore implementation.
*/
#define HGCM_MSG_F_PROCESSED (0x00000001)
#define HGCM_MSG_F_WAIT (0x00000002)
#define HGCM_MSG_F_IN_PROCESS (0x00000004)
{
m_fu32Flags = 0;
}
{
if (m_pThread)
{
}
}
/*
* HGCMThread implementation.
*/
{
int rc = VINF_SUCCESS;
return rc;
}
HGCMThread::HGCMThread ()
:
m_pfnThread (NULL),
m_eventThread (0),
m_eventSend (0),
m_fu32ThreadFlags (0),
m_pFreeHead (NULL),
m_pFreeTail (NULL),
m_handle (0)
{
}
HGCMThread::~HGCMThread ()
{
/*
* Free resources allocated for the thread.
*/
if (RTCritSectIsInitialized (&m_critsect))
{
}
if (m_eventSend)
{
}
if (m_eventThread)
{
}
return;
}
int HGCMThread::WaitForTermination (void)
{
int rc = VINF_SUCCESS;
LogFlowFunc(("\n"));
if (m_thread != NIL_RTTHREAD)
{
}
return rc;
}
int HGCMThread::Initialize (HGCMTHREADHANDLE handle, const char *pszThreadName, PFNHGCMTHREAD pfnThread, void *pvUser)
{
int rc = VINF_SUCCESS;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
rc = RTThreadCreate (&thread, hgcmWorkerThreadFunc, this, 0, /* default stack size; some service may need quite a bit */
if (RT_SUCCESS(rc))
{
/* Wait until the thread is ready. */
}
else
{
Log(("hgcmThreadCreate: FAILURE: Can't start worker thread.\n"));
}
}
else
{
Log(("hgcmThreadCreate: FAILURE: Can't init a critical section for a hgcm worker thread.\n"));
}
}
else
{
Log(("hgcmThreadCreate: FAILURE: Can't create an event semaphore for a sent messages.\n"));
m_eventSend = 0;
}
}
else
{
Log(("hgcmThreadCreate: FAILURE: Can't create an event semaphore for a hgcm worker thread.\n"));
m_eventThread = 0;
}
return rc;
}
inline int HGCMThread::Enter (void)
{
#ifdef LOG_ENABLED
if (RT_FAILURE(rc))
{
}
#endif /* LOG_ENABLED */
return rc;
}
inline void HGCMThread::Leave (void)
{
}
int HGCMThread::MsgAlloc (HGCMMSGHANDLE *pHandle, uint32_t u32MsgId, PFNHGCMNEWMSGALLOC pfnNewMessage)
{
int rc = VINF_SUCCESS;
bool fFromFreeList = false;
{
/* We have to allocate a new memory block. */
{
rc = VERR_NO_MEMORY;
}
}
if (RT_SUCCESS(rc))
{
/* Initialize just allocated message core */
/* and the message specific data. */
pmsg->Initialize ();
/** Get handle of the message. The message will be also referenced
* until the handle is deleted.
*/
if (fFromFreeList)
{
/* Message was referenced in the free list, now dereference it. */
pmsg->Dereference ();
}
}
return rc;
}
{
int rc = VINF_SUCCESS;
LogFlow(("HGCMThread::MsgPost: thread = %p, pMsg = %p, pfnCallback = %p\n", this, pMsg, pfnCallback));
if (RT_SUCCESS(rc))
{
if (fWait)
{
}
/* Insert the message to the queue tail. */
if (m_pMsgInputQueueTail)
{
}
else
{
}
Leave ();
LogFlow(("HGCMThread::MsgPost: going to inform the thread %p about message, fWait = %d\n", this, fWait));
/* Inform the worker thread that there is a message. */
LogFlow(("HGCMThread::MsgPost: event signalled\n"));
if (fWait)
{
/* Immediately check if the message has been processed. */
{
/* Poll infrequently to make sure no completed message has been missed. */
{
}
}
/* 'Our' message has been processed, so should reset the semaphore.
* There is still possible that another message has been processed
* and the semaphore has been signalled again.
* Reset only if there are no other messages completed.
*/
Assert(c >= 0);
if (c == 0)
{
}
}
}
return rc;
}
{
int rc = VINF_SUCCESS;
for (;;)
{
{
break;
}
if (m_pMsgInputQueueHead)
{
/* Move the message to the m_pMsgInProcessHead list */
if (RT_FAILURE(rc))
{
break;
}
/* Remove the message from the head of Queue list. */
if (m_pMsgInputQueueHead->m_pNext)
{
}
else
{
}
/* Insert the message to the tail of the m_pMsgInProcessHead list. */
if (m_pMsgInProcessTail)
{
}
else
{
}
Leave ();
/* Return the message to the caller. */
break;
}
/* Wait for an event. */
}
return rc;
}
{
int rc = VINF_SUCCESS;
AssertReleaseMsg((pMsg->m_fu32Flags & HGCM_MSG_F_IN_PROCESS) != 0, ("%p %x\n", pMsg, pMsg->m_fu32Flags));
if (pMsg->m_pfnCallback)
{
/** @todo call callback with error code in MsgPost in case of errors */
}
/* Message processing has been completed. */
if (RT_SUCCESS(rc))
{
/* Remove the message from the InProcess queue. */
{
}
else
{
}
{
}
else
{
}
if (fWaited)
{
/* This should be done before setting the HGCM_MSG_F_PROCESSED flag. */
}
/* The message is now completed. */
Leave ();
if (fWaited)
{
/* Wake up all waiters. so they can decide if their message has been processed. */
}
}
return;
}
/*
* Thread API. Public interface.
*/
int hgcmThreadCreate (HGCMTHREADHANDLE *pHandle, const char *pszThreadName, PFNHGCMTHREAD pfnThread, void *pvUser)
{
int rc = VINF_SUCCESS;
LogFlow(("MAIN::hgcmThreadCreate\n"));
HGCMTHREADHANDLE handle = 0;
/* Allocate memory for a new thread object. */
if (pThread)
{
/* Put just created object to pool and obtain handle for it. */
/* Initialize the object. */
}
else
{
Log(("hgcmThreadCreate: FAILURE: Can't allocate memory for a hgcm worker thread.\n"));
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
}
else
{
if (handle != 0)
{
/* Delete allocated handle, this will also free the object memory. */
}
}
return rc;
}
{
int rc = VERR_INVALID_HANDLE;
if (pThread)
{
}
return rc;
}
int hgcmMsgAlloc (HGCMTHREADHANDLE hThread, HGCMMSGHANDLE *pHandle, uint32_t u32MsgId, PFNHGCMNEWMSGALLOC pfnNewMessage)
{
LogFlow(("hgcmMsgAlloc: thread handle = 0x%08X, pHandle = %p, sizeof (HGCMMsgCore) = %d\n", hThread, pHandle, sizeof (HGCMMsgCore)));
if (!pHandle)
{
return VERR_INVALID_PARAMETER;
}
int rc = VINF_SUCCESS;
if (!pThread)
{
}
else
{
}
return rc;
}
{
LogFlow(("MAIN::hgcmMsgPostInternal: hMsg = 0x%08X, pfnCallback = %p, fWait = %d\n", hMsg, pfnCallback, fWait));
int rc = VINF_SUCCESS;
if (!pMsg)
{
}
else
{
}
return rc;
}
/* Post message to worker thread with a flag indication if this is a Send or Post.
*
* @thread any
*/
{
if (RT_SUCCESS(rc))
{
}
return rc;
}
/* Send message to worker thread. Sending thread will block until message is processed.
*
* @thread any
*/
{
}
{
{
return VERR_INVALID_PARAMETER;
}
int rc = VINF_SUCCESS;
if (!pThread)
{
}
else
{
}
return rc;
}
{
if (!pMsg)
{
return;
}
return;
}
int hgcmThreadInit (void)
{
int rc = VINF_SUCCESS;
LogFlow(("MAIN::hgcmThreadInit\n"));
/** @todo error processing. */
rc = hgcmObjInit ();
return rc;
}
void hgcmThreadUninit (void)
{
hgcmObjUninit ();
}