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(¤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 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