// RequestPort.cpp
 
#include <new>
 
#include "AutoDeleter.h"
#include "Debug.h"
#include "Request.h"
#include "RequestHandler.h"
#include "RequestPort.h"
 
using std::nothrow;
 
// TODO: Limit the stacking of requests?
 
// AllocatorNode
struct RequestPort::AllocatorNode {
	AllocatorNode(Port* port) : allocator(port), previous(NULL) {}
 
	RequestAllocator	allocator;
	AllocatorNode*		previous;
};
 
 
// constructor
RequestPort::RequestPort(int32 size)
	: fPort(size),
	  fCurrentAllocatorNode(NULL)
{
}
 
// constructor
RequestPort::RequestPort(const Port::Info* info)
	: fPort(info),
	  fCurrentAllocatorNode(NULL)
{
}
 
// destructor
RequestPort::~RequestPort()
{
	while (fCurrentAllocatorNode)
		_PopAllocator();
}
 
// Close
void
RequestPort::Close()
{
	fPort.Close();
}
 
// InitCheck
status_t
RequestPort::InitCheck() const
{
	return fPort.InitCheck();
}
 
// GetPort
Port*
RequestPort::GetPort()
{
	return &fPort;
}
 
// GetPortInfo
const Port::Info*
RequestPort::GetPortInfo() const
{
	return fPort.GetInfo();
}
 
// SendRequest
status_t
RequestPort::SendRequest(RequestAllocator* allocator)
{
	// check initialization and parameters
	if (InitCheck() != B_OK)
		RETURN_ERROR(InitCheck());
	if (!allocator || allocator->GetRequest() == NULL
		|| allocator->GetRequestSize() < (int32)sizeof(Request)) {
		RETURN_ERROR(B_BAD_VALUE);
	}
	allocator->FinishDeferredInit();
//PRINT(("RequestPort::SendRequest(%lu)\n", allocator->GetRequest()->GetType()));
#if USER && !KERNEL_EMU
	if (!is_userland_request(allocator->GetRequest()->GetType())) {
		ERROR(("RequestPort::SendRequest(%" B_PRId32 "): request is not a "
			"userland request\n", allocator->GetRequest()->GetType()));
		debugger("Request is not a userland request.");
	}
#else
	if (!is_kernel_request(allocator->GetRequest()->GetType())) {
		ERROR(("RequestPort::SendRequest(%" B_PRId32 "): request is not a "
			"kernel request\n", allocator->GetRequest()->GetType()));
		debugger("Request is not a kernel request.");
	}
#endif
	RETURN_ERROR(fPort.Send(allocator->GetRequest(),
		allocator->GetRequestSize()));
}
 
// SendRequest
status_t
RequestPort::SendRequest(RequestAllocator* allocator,
	RequestHandler* handler, Request** reply, bigtime_t timeout)
{
	status_t error = SendRequest(allocator);
	if (error != B_OK)
		return error;
	return HandleRequests(handler, reply, timeout);
}
 
// ReceiveRequest
//
// The caller is responsible for calling ReleaseRequest() with the request.
status_t
RequestPort::ReceiveRequest(Request** request, bigtime_t timeout)
{
	// check initialization and parameters
	if (InitCheck() != B_OK)
		RETURN_ERROR(InitCheck());
	if (!request)
		RETURN_ERROR(B_BAD_VALUE);
 
	// allocate a request allocator
	AllocatorNode* node = new(nothrow) AllocatorNode(&fPort);
	if (!node)
		RETURN_ERROR(B_NO_MEMORY);
	ObjectDeleter<AllocatorNode> deleter(node);
 
	// receive the message
	status_t error = node->allocator.ReadRequest(timeout);
	if (error != B_OK) {
		if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
			RETURN_ERROR(error);
		return error;
	}
 
	// allocate the request
	if (error != B_OK)
		RETURN_ERROR(error);
 
	// everything went fine: push the allocator
	*request = node->allocator.GetRequest();
	node->previous = fCurrentAllocatorNode;
	fCurrentAllocatorNode = node;
	deleter.Detach();
//PRINT(("RequestPort::RequestReceived(%lu)\n", (*request)->GetType()));
	return B_OK;
}
 
// HandleRequests
//
// If request is not NULL, the caller is responsible for calling
// ReleaseRequest() with the request. If it is NULL, the request will already
// be gone, when the method returns.
status_t
RequestPort::HandleRequests(RequestHandler* handler, Request** request,
	bigtime_t timeout)
{
	// check initialization and parameters
	if (InitCheck() != B_OK)
		RETURN_ERROR(InitCheck());
	if (!handler)
		RETURN_ERROR(B_BAD_VALUE);
	handler->SetPort(this);
	Request* currentRequest = NULL;
	do {
		if (currentRequest)
			ReleaseRequest(currentRequest);
		status_t error = ReceiveRequest(&currentRequest, timeout);
		if (error != B_OK)
			return error;
		// handle the request
		error = handler->HandleRequest(currentRequest);
		if (error != B_OK) {
			ReleaseRequest(currentRequest);
			RETURN_ERROR(error);
		}
	} while (!handler->IsDone());
	if (request)
		*request = currentRequest;
	else
		ReleaseRequest(currentRequest);
	return B_OK;
}
 
// ReleaseRequest
void
RequestPort::ReleaseRequest(Request* request)
{
	if (request && fCurrentAllocatorNode
		&& request == fCurrentAllocatorNode->allocator.GetRequest()) {
		_PopAllocator();
	}
}
 
// _PopAllocator
void
RequestPort::_PopAllocator()
{
	if (fCurrentAllocatorNode) {
		AllocatorNode* node = fCurrentAllocatorNode->previous;
		delete fCurrentAllocatorNode;
		fCurrentAllocatorNode = node;
	}
}
 

V547 Expression 'error != ((int) 0)' is always false.