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