#include <new>
#include "AutoDeleter.h"
#include "Debug.h"
#include "Request.h"
#include "RequestHandler.h"
#include "RequestPort.h"
using std::nothrow;
struct RequestPort::AllocatorNode {
AllocatorNode(Port* port) : allocator(port), previous(NULL) {}
RequestAllocator allocator;
AllocatorNode* previous;
};
RequestPort::RequestPort(int32 size)
: fPort(size),
fCurrentAllocatorNode(NULL)
{
}
RequestPort::RequestPort(const Port::Info* info)
: fPort(info),
fCurrentAllocatorNode(NULL)
{
}
RequestPort::~RequestPort()
{
while (fCurrentAllocatorNode)
_PopAllocator();
}
void
RequestPort::Close()
{
fPort.Close();
}
status_t
RequestPort::InitCheck() const
{
return fPort.InitCheck();
}
Port*
RequestPort::GetPort()
{
return &fPort;
}
const Port::Info*
RequestPort::GetPortInfo() const
{
return fPort.GetInfo();
}
status_t
RequestPort::SendRequest(RequestAllocator* allocator)
{
if (InitCheck() != B_OK)
RETURN_ERROR(InitCheck());
if (!allocator || allocator->GetRequest() == NULL
|| allocator->GetRequestSize() < (int32)sizeof(Request)) {
RETURN_ERROR(B_BAD_VALUE);
}
allocator->FinishDeferredInit();
#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()));
}
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);
}
status_t
RequestPort::ReceiveRequest(Request** request, bigtime_t timeout)
{
if (InitCheck() != B_OK)
RETURN_ERROR(InitCheck());
if (!request)
RETURN_ERROR(B_BAD_VALUE);
AllocatorNode* node = new(nothrow) AllocatorNode(&fPort);
if (!node)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<AllocatorNode> deleter(node);
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;
}
if (error != B_OK)
RETURN_ERROR(error);
*request = node->allocator.GetRequest();
node->previous = fCurrentAllocatorNode;
fCurrentAllocatorNode = node;
deleter.Detach();
return B_OK;
}
status_t
RequestPort::HandleRequests(RequestHandler* handler, Request** request,
bigtime_t timeout)
{
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(¤tRequest, timeout);
if (error != B_OK)
return error;
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;
}
void
RequestPort::ReleaseRequest(Request* request)
{
if (request && fCurrentAllocatorNode
&& request == fCurrentAllocatorNode->allocator.GetRequest()) {
_PopAllocator();
}
}
void
RequestPort::_PopAllocator()
{
if (fCurrentAllocatorNode) {
AllocatorNode* node = fCurrentAllocatorNode->previous;
delete fCurrentAllocatorNode;
fCurrentAllocatorNode = node;
}
}