xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/private/RequestPort.cpp (revision 56430ad8002b8fd1ac69b590e9cc130de6d9e852)
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() == 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
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 
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.
158 status_t
159 RequestPort::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
190 void
191 RequestPort::ReleaseRequest(Request* request)
192 {
193 	if (request && fCurrentAllocatorNode
194 		&& request == fCurrentAllocatorNode->allocator.GetRequest()) {
195 		_PopAllocator();
196 	}
197 }
198 
199 // _PopAllocator
200 void
201 RequestPort::_PopAllocator()
202 {
203 	if (fCurrentAllocatorNode) {
204 		AllocatorNode* node = fCurrentAllocatorNode->previous;
205 		delete fCurrentAllocatorNode;
206 		fCurrentAllocatorNode = node;
207 	}
208 }
209 
210