1// RequestPort.cpp
2
3#include <new>
4
5#include "AutoDeleter.h"
6#include "Debug.h"
7#include "Request.h"
8#include "RequestHandler.h"
9#include "RequestPort.h"
10
11// TODO: Limit the stacking of requests?
12
13// AllocatorNode
14struct RequestPort::AllocatorNode {
15	AllocatorNode(Port* port) : allocator(port), previous(NULL) {}
16
17	RequestAllocator	allocator;
18	AllocatorNode*		previous;
19};
20
21
22// constructor
23RequestPort::RequestPort(int32 size)
24	: fPort(size),
25	  fCurrentAllocatorNode(NULL)
26{
27}
28
29// constructor
30RequestPort::RequestPort(const Port::Info* info)
31	: fPort(info),
32	  fCurrentAllocatorNode(NULL)
33{
34}
35
36// destructor
37RequestPort::~RequestPort()
38{
39	while (fCurrentAllocatorNode)
40		_PopAllocator();
41}
42
43// Close
44void
45RequestPort::Close()
46{
47	fPort.Close();
48}
49
50// InitCheck
51status_t
52RequestPort::InitCheck() const
53{
54	return fPort.InitCheck();
55}
56
57// GetPort
58Port*
59RequestPort::GetPort()
60{
61	return &fPort;
62}
63
64// GetPortInfo
65const Port::Info*
66RequestPort::GetPortInfo() const
67{
68	return fPort.GetInfo();
69}
70
71// SendRequest
72status_t
73RequestPort::SendRequest(RequestAllocator* allocator)
74{
75	// check initialization and parameters
76	if (InitCheck() != B_OK)
77		RETURN_ERROR(InitCheck());
78	if (!allocator || allocator->GetRequest() != fPort.GetBuffer()
79		|| allocator->GetRequestSize() < (int32)sizeof(Request)
80		|| allocator->GetRequestSize() > fPort.GetCapacity()) {
81		RETURN_ERROR(B_BAD_VALUE);
82	}
83	allocator->FinishDeferredInit();
84//PRINT(("RequestPort::SendRequest(%lu)\n", allocator->GetRequest()->GetType()));
85#if USER && !KERNEL_EMU
86	if (!is_userland_request(allocator->GetRequest()->GetType())) {
87		ERROR(("RequestPort::SendRequest(%lu): request is not a userland "
88			"request\n", allocator->GetRequest()->GetType()));
89		debugger("Request is not a userland request.");
90	}
91#else
92	if (!is_kernel_request(allocator->GetRequest()->GetType())) {
93		ERROR(("RequestPort::SendRequest(%lu): request is not a userland "
94			"request\n", allocator->GetRequest()->GetType()));
95		debugger("Request is not a userland request.");
96	}
97#endif
98	RETURN_ERROR(fPort.Send(allocator->GetRequestSize()));
99}
100
101// SendRequest
102status_t
103RequestPort::SendRequest(RequestAllocator* allocator,
104	RequestHandler* handler, Request** reply, bigtime_t timeout)
105{
106	status_t error = SendRequest(allocator);
107	if (error != B_OK)
108		return error;
109	return HandleRequests(handler, reply, timeout);
110}
111
112// ReceiveRequest
113//
114// The caller is responsible for calling ReleaseRequest() with the request.
115status_t
116RequestPort::ReceiveRequest(Request** request, bigtime_t timeout)
117{
118	// check initialization and parameters
119	if (InitCheck() != B_OK)
120		RETURN_ERROR(InitCheck());
121	if (!request)
122		RETURN_ERROR(B_BAD_VALUE);
123	// allocate a request allocator
124	AllocatorNode* node = new(nothrow) AllocatorNode(&fPort);
125	if (!node)
126		RETURN_ERROR(B_NO_MEMORY);
127	ObjectDeleter<AllocatorNode> deleter(node);
128	// receive the message
129	status_t error = fPort.Receive(timeout);
130	if (error != B_OK) {
131		if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
132			RETURN_ERROR(error);
133		return error;
134	}
135	// allocate the request
136	error = node->allocator.ReadRequest();
137	if (error != B_OK)
138		RETURN_ERROR(error);
139	// everything went fine: push the allocator
140	*request = node->allocator.GetRequest();
141	node->previous = fCurrentAllocatorNode;
142	fCurrentAllocatorNode = node;
143	deleter.Detach();
144//PRINT(("RequestPort::RequestReceived(%lu)\n", (*request)->GetType()));
145	return B_OK;
146}
147
148// HandleRequests
149//
150// If request is not NULL, the caller is responsible for calling
151// ReleaseRequest() with the request. If it is NULL, the request will already
152// be gone, when the method returns.
153status_t
154RequestPort::HandleRequests(RequestHandler* handler, Request** request,
155	bigtime_t timeout)
156{
157	// check initialization and parameters
158	if (InitCheck() != B_OK)
159		RETURN_ERROR(InitCheck());
160	if (!handler)
161		RETURN_ERROR(B_BAD_VALUE);
162	handler->SetPort(this);
163	Request* currentRequest = NULL;
164	do {
165		if (currentRequest)
166			ReleaseRequest(currentRequest);
167		status_t error = ReceiveRequest(&currentRequest, timeout);
168		if (error != B_OK)
169			return error;
170		// handle the request
171		error = handler->HandleRequest(currentRequest);
172		if (error != B_OK) {
173			ReleaseRequest(currentRequest);
174			RETURN_ERROR(error);
175		}
176	} while (!handler->IsDone());
177	if (request)
178		*request = currentRequest;
179	else
180		ReleaseRequest(currentRequest);
181	return B_OK;
182}
183
184// ReleaseRequest
185void
186RequestPort::ReleaseRequest(Request* request)
187{
188	if (request && fCurrentAllocatorNode
189		&& request == fCurrentAllocatorNode->allocator.GetRequest()) {
190		_PopAllocator();
191	}
192}
193
194// _PopAllocator
195void
196RequestPort::_PopAllocator()
197{
198	if (fCurrentAllocatorNode) {
199		AllocatorNode* node = fCurrentAllocatorNode->previous;
200		delete fCurrentAllocatorNode;
201		fCurrentAllocatorNode = node;
202	}
203}
204
205