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