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