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