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 {
AllocatorNodeRequestPort::AllocatorNode17 AllocatorNode(Port* port) : allocator(port), previous(NULL) {}
18
19 RequestAllocator allocator;
20 AllocatorNode* previous;
21 };
22
23
24 // constructor
RequestPort(int32 size)25 RequestPort::RequestPort(int32 size)
26 : fPort(size),
27 fCurrentAllocatorNode(NULL)
28 {
29 }
30
31 // constructor
RequestPort(const Port::Info * info)32 RequestPort::RequestPort(const Port::Info* info)
33 : fPort(info),
34 fCurrentAllocatorNode(NULL)
35 {
36 }
37
38 // destructor
~RequestPort()39 RequestPort::~RequestPort()
40 {
41 while (fCurrentAllocatorNode)
42 _PopAllocator();
43 }
44
45 // Close
46 void
Close()47 RequestPort::Close()
48 {
49 fPort.Close();
50 }
51
52 // InitCheck
53 status_t
InitCheck() const54 RequestPort::InitCheck() const
55 {
56 return fPort.InitCheck();
57 }
58
59 // GetPort
60 Port*
GetPort()61 RequestPort::GetPort()
62 {
63 return &fPort;
64 }
65
66 // GetPortInfo
67 const Port::Info*
GetPortInfo() const68 RequestPort::GetPortInfo() const
69 {
70 return fPort.GetInfo();
71 }
72
73 // SendRequest
74 status_t
SendRequest(RequestAllocator * allocator)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
SendRequest(RequestAllocator * allocator,RequestHandler * handler,Request ** reply,bigtime_t timeout)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
ReceiveRequest(Request ** request,bigtime_t timeout)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
HandleRequests(RequestHandler * handler,Request ** request,bigtime_t timeout)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(¤tRequest, 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
ReleaseRequest(Request * request)191 RequestPort::ReleaseRequest(Request* request)
192 {
193 if (request && fCurrentAllocatorNode
194 && request == fCurrentAllocatorNode->allocator.GetRequest()) {
195 _PopAllocator();
196 }
197 }
198
199 // _PopAllocator
200 void
_PopAllocator()201 RequestPort::_PopAllocator()
202 {
203 if (fCurrentAllocatorNode) {
204 AllocatorNode* node = fCurrentAllocatorNode->previous;
205 delete fCurrentAllocatorNode;
206 fCurrentAllocatorNode = node;
207 }
208 }
209
210