diff --git a/headers/private/app/LinkMsgReader.h b/headers/private/app/LinkMsgReader.h index 21621155a3..8fd5310764 100644 --- a/headers/private/app/LinkMsgReader.h +++ b/headers/private/app/LinkMsgReader.h @@ -1,71 +1,56 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, Haiku -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// File Name: LinkMsgReader.h -// Author: DarkWyrm -// Pahtz -// Description: Class for receiving low-overhead port-based messages -// -//------------------------------------------------------------------------------ +/* + * Copyright 2001-2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Pahtz + */ #ifndef _LINKMSGREADER_H #define _LINKMSGREADER_H #include -class LinkMsgReader -{ -public: - LinkMsgReader(port_id port); - virtual ~LinkMsgReader(void); - virtual void SetPort(port_id port); - virtual port_id GetPort(void); - - virtual status_t GetNextMessage(int32 *code, bigtime_t timeout = B_INFINITE_TIMEOUT); - virtual status_t Read(void *data, ssize_t size); - virtual status_t ReadString(char **string); - template status_t Read(Type *data) - { - return Read(data, sizeof(Type)); - } - -protected: - virtual status_t ReadFromPort(bigtime_t timeout); - virtual status_t AdjustReplyBuffer(bigtime_t timeout); - void ResetBuffer(); - - port_id fReceivePort; - - char *fRecvBuffer; +//namespace BPrivate { - int32 fRecvPosition; //current read position +class LinkMsgReader { + public: + LinkMsgReader(port_id port); + virtual ~LinkMsgReader(void); - int32 fRecvStart; //start of current message - - int32 fRecvBufferSize; + void SetPort(port_id port); + port_id Port(void) { return fReceivePort; } - int32 fDataSize; //size of data in recv buffer - int32 fReplySize; //size of current reply message - - status_t fReadError; //Read failed for current message + status_t GetNextMessage(int32 *code, bigtime_t timeout = B_INFINITE_TIMEOUT); + status_t Read(void *data, ssize_t size); + status_t ReadString(char **string); + template status_t Read(Type *data) + { + return Read(data, sizeof(Type)); + } + + protected: + virtual status_t ReadFromPort(bigtime_t timeout); + virtual status_t AdjustReplyBuffer(bigtime_t timeout); + void ResetBuffer(); + + port_id fReceivePort; + + char *fRecvBuffer; + + int32 fRecvPosition; //current read position + + int32 fRecvStart; //start of current message + + int32 fRecvBufferSize; + + int32 fDataSize; //size of data in recv buffer + int32 fReplySize; //size of current reply message + + status_t fReadError; //Read failed for current message }; +//} // namespace BPrivate + #endif diff --git a/headers/private/app/LinkMsgSender.h b/headers/private/app/LinkMsgSender.h index ce6cdca83b..e3b5ef7d9e 100644 --- a/headers/private/app/LinkMsgSender.h +++ b/headers/private/app/LinkMsgSender.h @@ -1,82 +1,63 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// File Name: LinkMsgSender.h -// Author: DarkWyrm -// Pahtz -// Description: Class for sending low-overhead port-based messaging -// -//------------------------------------------------------------------------------ +/* + * Copyright 2001-2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Pahtz + * Axel Dörfler, axeld@pinc-software.de + */ #ifndef LINKMSGSENDER_H #define LINKMSGSENDER_H + #include -class LinkMsgSender -{ -public: - LinkMsgSender(port_id sendport); - virtual ~LinkMsgSender(void); - status_t StartMessage(int32 code); - void CancelMessage(void); - status_t EndMessage(void); - - status_t Flush(bigtime_t timeout = B_INFINITE_TIMEOUT); +//namespace BPrivate { - // see BPrivate::BAppServerLink which inherits from BPortLink - //status_t FlushWithReply(int32 *code); +class LinkMsgSender { + public: + LinkMsgSender(port_id sendport); + virtual ~LinkMsgSender(void); - void SetPort(port_id port); - port_id GetPort(); + void SetPort(port_id port); + port_id Port() { return fPort; } - status_t Attach(const void *data, ssize_t size); - status_t AttachString(const char *string); - template status_t Attach(const Type& data) - { - return Attach(&data, sizeof(Type)); - } + status_t StartMessage(int32 code, size_t minSize = 0); + void CancelMessage(void); + status_t EndMessage(bool needsReply = false); -protected: - status_t FlushCompleted(ssize_t newbuffersize); - status_t AdjustReplyBuffer(bigtime_t timeout); - void ResetReplyBuffer(); - - port_id fSendPort; + status_t Flush(bigtime_t timeout = B_INFINITE_TIMEOUT, bool needsReply = false); - char *fSendBuffer; + // see BPrivate::BAppServerLink which inherits from BPortLink + //status_t FlushWithReply(int32 *code); - int32 fSendPosition; //current append position + status_t Attach(const void *data, size_t size); + status_t AttachString(const char *string); + template status_t Attach(const Type& data) + { + return Attach(&data, sizeof(Type)); + } - int32 fSendStart; //start of current message - - int32 fSendBufferSize; + protected: + size_t SpaceLeft() const { return fBufferSize - fCurrentEnd; } + size_t CurrentMessageSize() const { return fCurrentEnd - fCurrentStart; } - int32 fSendCount; //number of completed messages in buffer + status_t AdjustBuffer(size_t newBufferSize, char **_oldBuffer = NULL); + status_t FlushCompleted(size_t newBufferSize); - int32 fDataSize; //size of data in recv buffer - int32 fReplySize; //size of current reply message - - status_t fWriteError; //Attach failed for current message + port_id fPort; + + char *fBuffer; + size_t fBufferSize; + + uint32 fCurrentEnd; // current append position + uint32 fCurrentStart; // start of current message + + status_t fCurrentStatus; }; +//} // namespace BPrivate -#endif +#endif /* LINKMSGSENDER_H */ diff --git a/headers/private/app/PortLink.h b/headers/private/app/PortLink.h index 9d60bc0138..347dbc1526 100644 --- a/headers/private/app/PortLink.h +++ b/headers/private/app/PortLink.h @@ -1,34 +1,18 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, OpenBeOS -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// File Name: PortLink.h -// Author: DarkWyrm -// Pahtz -// Description: Class for low-overhead port-based messaging -// -//------------------------------------------------------------------------------ +/* + * Copyright 2001-2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm + * Pahtz + * Axel Dörfler, axeld@pinc-software.de + */ #ifndef _PORTLINK_H #define _PORTLINK_H #include +#include +#include /* Error checking rules: (for if you don't want to check every return code) @@ -45,48 +29,144 @@ */ -class LinkMsgReader; -class LinkMsgSender; -class BPortLink -{ -public: - BPortLink(port_id send = -1, port_id reply = -1); - virtual ~BPortLink(); +// ToDo: put this into the private namespace +//namespace BPrivate { - status_t StartMessage(int32 code); - void CancelMessage(); - status_t EndMessage(); +//class LinkMsgReader; +//class LinkMsgSender; - status_t Flush(bigtime_t timeout = B_INFINITE_TIMEOUT); - - void SetSendPort(port_id port); - port_id GetSendPort(); - - void SetReplyPort(port_id port); - port_id GetReplyPort(); +class BPortLink { + public: + BPortLink(port_id send = -1, port_id reply = -1); + virtual ~BPortLink(); - status_t Attach(const void *data, ssize_t size); - status_t AttachString(const char *string); - status_t AttachRegion(const BRegion ®ion); - status_t AttachShape(BShape &shape); - template status_t Attach(const Type& data) - { - return Attach(&data, sizeof(Type)); - } + // send methods - status_t GetNextReply(int32 *code, bigtime_t timeout = B_INFINITE_TIMEOUT); - status_t Read(void *data, ssize_t size); - status_t ReadString(char **string); - status_t ReadRegion(BRegion *region); - status_t ReadShape(BShape *shape); - template status_t Read(T *data) - { - return Read(data,sizeof(T)); - } - -protected: - LinkMsgReader *fReader; - LinkMsgSender *fSender; + void SetSendPort(port_id port); + port_id SendPort(); + + status_t StartMessage(int32 code, size_t minSize = 0); + void CancelMessage(); + status_t EndMessage(); + + status_t Flush(bigtime_t timeout = B_INFINITE_TIMEOUT, bool needsReply = false); + status_t Attach(const void *data, ssize_t size); + status_t AttachString(const char *string); + status_t AttachRegion(const BRegion ®ion); + status_t AttachShape(BShape &shape); + template status_t Attach(const Type& data); + + // receive methods + + void SetReplyPort(port_id port); + port_id ReplyPort(); + + status_t GetNextReply(int32 *code, bigtime_t timeout = B_INFINITE_TIMEOUT); + status_t Read(void *data, ssize_t size); + status_t ReadString(char **string); + status_t ReadRegion(BRegion *region); + status_t ReadShape(BShape *shape); + template status_t Read(Type *data); + + protected: + LinkMsgReader *fReader; + LinkMsgSender *fSender; }; -#endif +// sender inline functions + +inline void +BPortLink::SetSendPort(port_id port) +{ + fSender->SetPort(port); +} + +inline port_id +BPortLink::SendPort() +{ + return fSender->Port(); +} + +inline status_t +BPortLink::StartMessage(int32 code, size_t minSize) +{ + return fSender->StartMessage(code, minSize); +} + +inline status_t +BPortLink::EndMessage() +{ + return fSender->EndMessage(); +} + +inline void +BPortLink::CancelMessage() +{ + fSender->CancelMessage(); +} + +inline status_t +BPortLink::Flush(bigtime_t timeout, bool needsReply) +{ + return fSender->Flush(timeout, needsReply); +} + +inline status_t +BPortLink::Attach(const void *data, ssize_t size) +{ + return fSender->Attach(data, size); +} + +inline status_t +BPortLink::AttachString(const char *string) +{ + return fSender->AttachString(string); +} + +template status_t +BPortLink::Attach(const Type &data) +{ + return Attach(&data, sizeof(Type)); +} + +// #pragma mark - receiver inline functions + +inline void +BPortLink::SetReplyPort(port_id port) +{ + fReader->SetPort(port); +} + +inline port_id +BPortLink::ReplyPort() +{ + return fReader->Port(); +} + +inline status_t +BPortLink::GetNextReply(int32 *code, bigtime_t timeout) +{ + return fReader->GetNextMessage(code, timeout); +} + +inline status_t +BPortLink::Read(void *data, ssize_t size) +{ + return fReader->Read(data, size); +} + +inline status_t +BPortLink::ReadString(char **string) +{ + return fReader->ReadString(string); +} + +template status_t +BPortLink::Read(Type *data) +{ + return Read(data, sizeof(Type)); +} + +//} // namespace BPrivate + +#endif /* _PORTLINK_H */ diff --git a/src/kits/app/LinkMsgReader.cpp b/src/kits/app/LinkMsgReader.cpp index d5e62a8f85..54a68d56ee 100644 --- a/src/kits/app/LinkMsgReader.cpp +++ b/src/kits/app/LinkMsgReader.cpp @@ -30,9 +30,6 @@ extern const char *bstrcode(int32 code); # define STRACE(x) ; #endif -static const int32 kInitialReceiveBufferSize = 2048; -static const int32 kMaxReceiveBufferSize = 2048; - LinkMsgReader::LinkMsgReader(port_id port) : @@ -56,13 +53,6 @@ LinkMsgReader::SetPort(port_id port) } -port_id -LinkMsgReader::GetPort() -{ - return fReceivePort; -} - - status_t LinkMsgReader::GetNextMessage(int32 *code, bigtime_t timeout) { @@ -82,7 +72,7 @@ LinkMsgReader::GetNextMessage(int32 *code, bigtime_t timeout) remaining = fDataSize; header = (message_header *)fRecvBuffer; } else { - fRecvStart += fReplySize; //start of the next message + fRecvStart += fReplySize; // start of the next message fRecvPosition = fRecvStart; header = (message_header *)(fRecvBuffer + fRecvStart); } @@ -126,45 +116,46 @@ status_t LinkMsgReader::AdjustReplyBuffer(bigtime_t timeout) { // Here we take advantage of the compiler's dead-code elimination - if (kInitialReceiveBufferSize == kMaxReceiveBufferSize) { + if (kInitialBufferSize == kMaxBufferSize) { // fixed buffer size if (fRecvBuffer != NULL) return B_OK; - fRecvBuffer = (char *)malloc(kInitialReceiveBufferSize); + fRecvBuffer = (char *)malloc(kInitialBufferSize); if (fRecvBuffer == NULL) return B_NO_MEMORY; - fRecvBufferSize = kInitialReceiveBufferSize; + + fRecvBufferSize = kInitialBufferSize; } else { STRACE(("info: LinkMsgReader getting port_buffer_size().\n")); - ssize_t buffersize; + ssize_t bufferSize; if (timeout == B_INFINITE_TIMEOUT) - buffersize = port_buffer_size(fReceivePort); + bufferSize = port_buffer_size(fReceivePort); else - buffersize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout); - STRACE(("info: LinkMsgReader got port_buffer_size() = %ld.\n", buffersize)); + bufferSize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout); + STRACE(("info: LinkMsgReader got port_buffer_size() = %ld.\n", bufferSize)); - if (buffersize < 0) - return (status_t)buffersize; + if (bufferSize < 0) + return (status_t)bufferSize; // make sure our receive buffer is large enough - if (buffersize > fRecvBufferSize) { - if (buffersize <= kInitialReceiveBufferSize) - buffersize = kInitialReceiveBufferSize; + if (bufferSize > fRecvBufferSize) { + if (bufferSize <= (ssize_t)kInitialBufferSize) + bufferSize = (ssize_t)kInitialBufferSize; else - buffersize = (buffersize + B_PAGE_SIZE) - (buffersize % B_PAGE_SIZE); - if (buffersize > kMaxReceiveBufferSize) - return B_ERROR; //we can't continue + bufferSize = (bufferSize + B_PAGE_SIZE) - (bufferSize % B_PAGE_SIZE); + if (bufferSize > (ssize_t)kMaxBufferSize) + return B_ERROR; // we can't continue - STRACE(("info: LinkMsgReader setting receive buffersize to %ld.\n", buffersize)); - char *buffer = (char *)malloc(buffersize); + STRACE(("info: LinkMsgReader setting receive buffersize to %ld.\n", bufferSize)); + char *buffer = (char *)malloc(bufferSize); if (buffer == NULL) return B_NO_MEMORY; free(fRecvBuffer); fRecvBuffer = buffer; - fRecvBufferSize = buffersize; + fRecvBufferSize = bufferSize; } } @@ -182,34 +173,39 @@ LinkMsgReader::ReadFromPort(bigtime_t timeout) if (err < B_OK) return err; - int32 protocol; - ssize_t bytesread; + int32 code; + ssize_t bytesRead; + STRACE(("info: LinkMsgReader reading port %ld.\n", fReceivePort)); - if (timeout != B_INFINITE_TIMEOUT) { - do { - bytesread = read_port_etc(fReceivePort, &protocol, fRecvBuffer, - fRecvBufferSize, B_TIMEOUT, timeout); - } while(bytesread == B_INTERRUPTED); - } else { - do { - bytesread = read_port(fReceivePort, &protocol, fRecvBuffer, - fRecvBufferSize); - } while(bytesread == B_INTERRUPTED); + while (true) { + if (timeout != B_INFINITE_TIMEOUT) { + do { + bytesRead = read_port_etc(fReceivePort, &code, fRecvBuffer, + fRecvBufferSize, B_TIMEOUT, timeout); + } while (bytesRead == B_INTERRUPTED); + } else { + do { + bytesRead = read_port(fReceivePort, &code, fRecvBuffer, + fRecvBufferSize); + } while (bytesRead == B_INTERRUPTED); + } + + STRACE(("info: LinkMsgReader read %ld bytes.\n", bytesRead)); + if (bytesRead < B_OK) + return bytesRead; + + // we just ignore incorrect messages, and don't bother our caller + + if (code != kLinkCode) { + STRACE(("wrong port message %lx received.\n", code)); + continue; + } + + // port read seems to be valid + break; } - STRACE(("info: LinkMsgReader read %ld bytes.\n", bytesread)); - if (bytesread < B_OK) - return bytesread; - - // TODO: we only need AS_SERVER_PORTLINK when all OBOS uses Link messages - if (protocol != AS_SERVER_PORTLINK && protocol != AS_SERVER_SESSION) - return B_ERROR; - if (protocol == AS_SERVER_PORTLINK && bytesread != *((int32 *)fRecvBuffer)) - // should only be one message for PORTLINK so the size declared in the header - // (the first int32 in the header) should be the same as bytesread - return B_ERROR; - - fDataSize = bytesread; + fDataSize = bytesRead; return B_OK; } @@ -242,33 +238,36 @@ LinkMsgReader::Read(void *data, ssize_t size) status_t -LinkMsgReader::ReadString(char **string) +LinkMsgReader::ReadString(char **_string) { - status_t err; - int32 len = 0; + int32 length = 0; + status_t status; - err = Read(&len); - if (err < B_OK) - return err; + status = Read(&length); + if (status < B_OK) + return status; - if (len) { - *string = (char *)malloc(len); - if (*string == NULL) { - fRecvPosition -= sizeof(int32); //rewind the transaction + if (length > 0) { + char *string = (char *)malloc(length); + if (string == NULL) { + fRecvPosition -= sizeof(int32); // rewind the transaction return B_NO_MEMORY; } - err = Read(*string, len); - if (err < B_OK) { - free(*string); - *string = NULL; - fRecvPosition -= sizeof(int32); //rewind the transaction - return err; + status = Read(string, length); + if (status < B_OK) { + free(string); + fRecvPosition -= sizeof(int32); // rewind the transaction + return status; } - (*string)[len-1] = '\0'; + + // make sure the string is null terminated (although it already should be) + string[length - 1] = '\0'; + + *_string = string; return B_OK; } else { - fRecvPosition -= sizeof(int32); //rewind the transaction + fRecvPosition -= sizeof(int32); // rewind the transaction return B_ERROR; } } diff --git a/src/kits/app/LinkMsgSender.cpp b/src/kits/app/LinkMsgSender.cpp index 033dcc9b0f..3ff425d2ed 100644 --- a/src/kits/app/LinkMsgSender.cpp +++ b/src/kits/app/LinkMsgSender.cpp @@ -4,7 +4,7 @@ * * Authors: * Pahtz - * Axel Dörfler + * Axel Dörfler, axeld@pinc-software.de */ /** Class for low-overhead port-based messaging */ @@ -30,80 +30,92 @@ # define STRACE(x) ; #endif -//set Initial==Max for a fixed buffer size -static const int32 kInitialSendBufferSize = 2048; -static const int32 kMaxSendBufferSize = 2048; +static const size_t kWatermark = kInitialBufferSize - 24; + // if a message is started after this mark, the buffer is flushed automatically -LinkMsgSender::LinkMsgSender(port_id send) +LinkMsgSender::LinkMsgSender(port_id port) : - fSendPort(send), fSendBuffer(NULL), fSendPosition(0), fSendStart(0), - fSendBufferSize(0), fSendCount(0), fDataSize(0), - fReplySize(0), fWriteError(B_OK) + fPort(port), + fBuffer(NULL), + fBufferSize(0), + + fCurrentEnd(0), + fCurrentStart(0), + fCurrentStatus(B_OK) { } LinkMsgSender::~LinkMsgSender() { - free(fSendBuffer); + free(fBuffer); +} + + +void +LinkMsgSender::SetPort(port_id port) +{ + fPort = port; } status_t -LinkMsgSender::StartMessage(int32 code) +LinkMsgSender::StartMessage(int32 code, size_t minSize) { // end previous message if (EndMessage() < B_OK) CancelMessage(); - if (fSendBufferSize == 0) { - fSendBuffer = (char *)malloc(kInitialSendBufferSize); - if (fSendBuffer == NULL) { - fWriteError = B_NO_MEMORY; - return B_NO_MEMORY; - } - fSendBufferSize = kInitialSendBufferSize; + if (minSize > kMaxBufferSize - sizeof(message_header)) + return fCurrentStatus = B_BUFFER_OVERFLOW; + + minSize += sizeof(message_header); + + // Eventually flush buffer to make space for the new message. + // Note, we do not take the actual buffer size into account to not + // delay the time between buffer flushes too much. + if (fBufferSize > 0 && (minSize > SpaceLeft() || fCurrentStart >= kWatermark)) { + status_t status = Flush(); + if (status < B_OK) + return status; } - status_t err; - // must have space for at least size + code + flags - if (fSendBufferSize - fSendPosition < (int32)sizeof(message_header)) { - err = Flush(); // will set fSendPosition and fSendStart to 0 - if (err < B_OK) - return err; + if (minSize > fBufferSize) { + if (AdjustBuffer(minSize) != B_OK) + return fCurrentStatus = B_NO_MEMORY; } - message_header *header = (message_header *)(fSendBuffer + fSendStart); + message_header *header = (message_header *)(fBuffer + fCurrentStart); header->size = 0; // will be set later header->code = code; header->flags = 0; - STRACE(("info: LinkMsgSender buffered header %s [%lu %lu %lu].\n", - strcode(code), header->size, header->code, header->flags)); + STRACE(("info: LinkMsgSender buffered header %s (%lx) [%lu %lu %lu].\n", + strcode(code), code, header->size, header->code, header->flags)); - fSendPosition += sizeof(message_header); + fCurrentEnd += sizeof(message_header); return B_OK; } status_t -LinkMsgSender::EndMessage() +LinkMsgSender::EndMessage(bool needsReply) { - if (fSendPosition == fSendStart || fWriteError < B_OK) - return fWriteError; + if (fCurrentEnd == fCurrentStart || fCurrentStatus < B_OK) + return fCurrentStatus; // record the size of the message - message_header *header = (message_header *)(fSendBuffer + fSendStart); - header->size = fSendPosition - fSendStart; + message_header *header = (message_header *)(fBuffer + fCurrentStart); + header->size = CurrentMessageSize(); + if (needsReply) + header->flags |= needsReply; STRACE(("info: LinkMsgSender EndMessage() of size %ld.\n", header->size)); - fSendCount++; // increase the number of completed messages - - fSendStart = fSendPosition; // start of next new message - + // bump to start of next message + fCurrentStart = fCurrentEnd; return B_OK; } @@ -111,177 +123,161 @@ LinkMsgSender::EndMessage() void LinkMsgSender::CancelMessage() { - fSendPosition = fSendStart; - fWriteError = B_OK; + fCurrentEnd = fCurrentStart; + fCurrentStatus = B_OK; } status_t -LinkMsgSender::Attach(const void *data, ssize_t size) +LinkMsgSender::Attach(const void *data, size_t size) { - if (fWriteError < B_OK) - return fWriteError; + if (fCurrentStatus < B_OK) + return fCurrentStatus; - if (size <= 0) { - fWriteError = B_BAD_VALUE; - return B_BAD_VALUE; - } + if (size == 0) + return fCurrentStatus = B_BAD_VALUE; - if (fSendPosition == fSendStart) + if (fCurrentEnd == fCurrentStart) return B_NO_INIT; // need to call StartMessage() first - int32 remaining = fSendBufferSize - fSendPosition; - if (remaining < size) { + if (SpaceLeft() < size) { // we have to make space for the data - int32 total = size + (fSendPosition - fSendStart); - // resulting size of current message - - int32 newbuffersize; - if (total <= fSendBufferSize) - newbuffersize = fSendBufferSize; // no change - else if (total > kMaxSendBufferSize) { - fWriteError = B_BAD_VALUE; - return B_BAD_VALUE; - } else if (total <= kInitialSendBufferSize) - newbuffersize = kInitialSendBufferSize; - else - newbuffersize = (total + B_PAGE_SIZE) - (total % B_PAGE_SIZE); - - // FlushCompleted() to make space - status_t err; - err = FlushCompleted(newbuffersize); - if (err < B_OK) { - fWriteError = err; - return err; - } + status_t status = FlushCompleted(size + CurrentMessageSize()); + if (status < B_OK) + return fCurrentStatus = status; } - memcpy(fSendBuffer + fSendPosition, data, size); - fSendPosition += size; - return fWriteError; -} - - -status_t -LinkMsgSender::FlushCompleted(ssize_t newbuffersize) -{ - char *buffer = NULL; - if (newbuffersize == fSendBufferSize) - buffer = fSendBuffer; // keep existing buffer - else { - // create new larger buffer - buffer = (char *)malloc(newbuffersize); - if (buffer == NULL) - return B_NO_MEMORY; - } - - int32 position = fSendPosition; - int32 start = fSendStart; - fSendPosition = fSendStart; // trick to hide the incomplete message - - status_t err; - err = Flush(); - if (err < B_OK) { - fSendPosition = position; - if (buffer != fSendBuffer) - free(buffer); - return err; - } - - // move the incomplete message to the start of the buffer - fSendPosition = min_c(position - start, newbuffersize); - memcpy(buffer, fSendBuffer + start, fSendPosition); - - if (fSendBuffer != buffer) { - free(fSendBuffer); - fSendBuffer = buffer; - fSendBufferSize = newbuffersize; - } + memcpy(fBuffer + fCurrentEnd, data, size); + fCurrentEnd += size; return B_OK; } -void -LinkMsgSender::SetPort(port_id port) -{ - fSendPort = port; -} - - -port_id -LinkMsgSender::GetPort() -{ - return fSendPort; -} - - -status_t -LinkMsgSender::Flush(bigtime_t timeout) -{ - if (fWriteError < B_OK) - return fWriteError; - - EndMessage(); - if (fSendCount == 0) - return B_OK; - - STRACE(("info: LinkMsgSender Flush() waiting to send %ld messages of %ld bytes on port %ld.\n", fSendCount, fSendPosition, fSendPort)); - - // TODO: we only need AS_SERVER_PORTLINK when all OBOS uses LinkMsgSender - int32 protocol = (fSendCount > 1 ? AS_SERVER_SESSION : AS_SERVER_PORTLINK); - - status_t err; - if (timeout != B_INFINITE_TIMEOUT) { - do { - err = write_port_etc(fSendPort, protocol, fSendBuffer, - fSendPosition, B_RELATIVE_TIMEOUT, timeout); - } while(err == B_INTERRUPTED); - } else { - do { - err = write_port(fSendPort, protocol, fSendBuffer, fSendPosition); - } while(err == B_INTERRUPTED); - } - - if (err == B_OK) { - STRACE(("info: LinkMsgSender Flush() %ld messages total of %ld bytes on port %ld.\n", fSendCount, fSendPosition, fSendPort)); - fSendPosition = 0; - fSendStart = 0; - fSendCount = 0; - return B_OK; - } - - STRACE(("error info: LinkMsgSender Flush() failed for %ld bytes (%s) on port %ld.\n", fSendPosition, strerror(err), fSendPort)); - return err; -} - - status_t LinkMsgSender::AttachString(const char *string) { - status_t err; - if (string == NULL) { -// TODO: This whole comm thing is so broken.... if we're for some -// reason attaching a NULL string, and don't do it, the receiving -// party will still try to read a string!! The whole communication -// will be messed up if the stream does not contain what the client -// things it contains. This is a quick fix (but just for this -// particular problem). -Stephan -// return B_BAD_VALUE; + if (string == NULL) string = ""; + + int32 length = strlen(string) + 1; + status_t status = Attach(length); + if (status < B_OK) + return status; + + status = Attach(string, length); + if (status < B_OK) + fCurrentEnd -= sizeof(int32); // rewind the transaction + + return status; +} + + +status_t +LinkMsgSender::AdjustBuffer(size_t newSize, char **_oldBuffer) +{ + // make sure the new size is within bounds + if (newSize <= kInitialBufferSize) + newSize = kInitialBufferSize; + else if (newSize > kMaxBufferSize) + return B_BUFFER_OVERFLOW; + else if (newSize > kInitialBufferSize) + newSize = (newSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); + + char *buffer = NULL; + if (newSize == fBufferSize) { + // keep existing buffer + if (_oldBuffer) + *_oldBuffer = fBuffer; + return B_OK; } - int32 len = strlen(string) + 1; - err = Attach(len); - if (err < B_OK) + // create new larger buffer + buffer = (char *)malloc(newSize); + if (buffer == NULL) + return B_NO_MEMORY; + + if (_oldBuffer) + *_oldBuffer = fBuffer; + else + free(fBuffer); + + fBuffer = buffer; + fBufferSize = newSize; + return B_OK; +} + + +status_t +LinkMsgSender::FlushCompleted(size_t newBufferSize) +{ + // we need to hide the incomplete message so that it's not flushed + int32 end = fCurrentEnd; + int32 start = fCurrentStart; + fCurrentEnd = fCurrentStart; + + status_t status = Flush(); + if (status < B_OK) { + fCurrentEnd = end; + return status; + } + + char *oldBuffer = NULL; + status = AdjustBuffer(newBufferSize, &oldBuffer); + if (status != B_OK) + return status; + + // move the incomplete message to the start of the buffer + fCurrentEnd = end - start; + if (oldBuffer != fBuffer) { + memcpy(fBuffer, oldBuffer + start, fCurrentEnd); + free(oldBuffer); + } else + memmove(fBuffer, fBuffer + start, fCurrentEnd); + + return B_OK; +} + + +status_t +LinkMsgSender::Flush(bigtime_t timeout, bool needsReply) +{ + if (fCurrentStatus < B_OK) + return fCurrentStatus; + + EndMessage(needsReply); + if (fCurrentStart == 0) + return B_OK; + + STRACE(("info: LinkMsgSender Flush() waiting to send messages of %ld bytes on port %ld.\n", + fCurrentEnd, fPort)); + + status_t err; + if (timeout != B_INFINITE_TIMEOUT) { + do { + err = write_port_etc(fPort, kLinkCode, fBuffer, + fCurrentEnd, B_RELATIVE_TIMEOUT, timeout); + } while (err == B_INTERRUPTED); + } else { + do { + err = write_port(fPort, kLinkCode, fBuffer, fCurrentEnd); + } while (err == B_INTERRUPTED); + } + + if (err < B_OK) { + STRACE(("error info: LinkMsgSender Flush() failed for %ld bytes (%s) on port %ld.\n", + fCurrentEnd, strerror(err), fPort)); return err; + } - err = Attach(string, len); - if (err < B_OK) - fSendPosition -= sizeof(int32); // rewind the transaction + STRACE(("info: LinkMsgSender Flush() messages total of %ld bytes on port %ld.\n", + fCurrentEnd, fPort)); - return err; + fCurrentEnd = 0; + fCurrentStart = 0; + + return B_OK; } diff --git a/src/kits/app/PortLink.cpp b/src/kits/app/PortLink.cpp index b2b6ef5c9d..81726d9323 100644 --- a/src/kits/app/PortLink.cpp +++ b/src/kits/app/PortLink.cpp @@ -1,118 +1,43 @@ -//------------------------------------------------------------------------------ -// Copyright (c) 2001-2002, Haiku, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// File Name: PortLink.cpp -// Author: Pahtz -// Description: Class for low-overhead port-based messaging -// -//------------------------------------------------------------------------------ +/* + * Copyright 2001-2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Pahtz + * Axel Dörfler, axeld@pinc-software.de + */ + +/** Class for low-overhead port-based messaging */ + #include #include #include #include #include - #include #include #include #include -BPortLink::BPortLink(port_id send, port_id receive) : - fReader(new LinkMsgReader(receive)), fSender(new LinkMsgSender(send)) + +BPortLink::BPortLink(port_id send, port_id receive) + : + fReader(new LinkMsgReader(receive)), + fSender(new LinkMsgSender(send)) { } + BPortLink::~BPortLink() { delete fReader; delete fSender; } -status_t BPortLink::StartMessage(int32 code) -{ - return fSender->StartMessage(code); -} -status_t BPortLink::EndMessage() -{ - return fSender->EndMessage(); -} - -void BPortLink::CancelMessage() -{ - fSender->CancelMessage(); -} - -status_t BPortLink::Attach(const void *data, ssize_t size) -{ - return fSender->Attach(data,size); -} - -void BPortLink::SetSendPort( port_id port ) -{ - fSender->SetPort(port); -} - -port_id BPortLink::GetSendPort() -{ - return fSender->GetPort(); -} - -void BPortLink::SetReplyPort( port_id port ) -{ - fReader->SetPort(port); -} - -port_id BPortLink::GetReplyPort() -{ - return fReader->GetPort(); -} - -status_t BPortLink::Flush(bigtime_t timeout) -{ - return fSender->Flush(timeout); -} - -status_t BPortLink::GetNextReply(int32 *code, bigtime_t timeout) -{ - return fReader->GetNextMessage(code,timeout); -} - -status_t BPortLink::Read(void *data, ssize_t size) -{ - return fReader->Read(data,size); -} - -status_t BPortLink::ReadString(char **string) -{ - return fReader->ReadString(string); -} - -status_t BPortLink::AttachString(const char *string) -{ - return fSender->AttachString(string); -} - -status_t BPortLink::ReadRegion(BRegion *region) +status_t +BPortLink::ReadRegion(BRegion *region) { fReader->Read(®ion->count, sizeof(long)); fReader->Read(®ion->bound, sizeof(clipping_rect)); @@ -120,14 +45,18 @@ status_t BPortLink::ReadRegion(BRegion *region) return fReader->Read(region->data, region->count * sizeof(clipping_rect)); } -status_t BPortLink::AttachRegion(const BRegion ®ion) + +status_t +BPortLink::AttachRegion(const BRegion ®ion) { fSender->Attach(®ion.count, sizeof(long)); fSender->Attach(®ion.bound, sizeof(clipping_rect)); return fSender->Attach(region.data, region.count * sizeof(clipping_rect)); } -status_t BPortLink::ReadShape(BShape *shape) + +status_t +BPortLink::ReadShape(BShape *shape) { int32 opCount, ptCount; fReader->Read(&opCount, sizeof(int32)); @@ -143,7 +72,9 @@ status_t BPortLink::ReadShape(BShape *shape) return B_OK; } -status_t BPortLink::AttachShape(BShape &shape) + +status_t +BPortLink::AttachShape(BShape &shape) { int32 opCount, ptCount; uint32 *opList; diff --git a/src/kits/app/ServerMemIO.cpp b/src/kits/app/ServerMemIO.cpp index f7ef8e8054..c350c3a5dd 100644 --- a/src/kits/app/ServerMemIO.cpp +++ b/src/kits/app/ServerMemIO.cpp @@ -49,7 +49,7 @@ ServerMemIO::ServerMemIO(size_t size) BPrivate::BAppServerLink link; link.StartMessage(AS_ACQUIRE_SERVERMEM); link.Attach(size); - link.Attach(link.GetReplyPort()); + link.Attach(link.ReplyPort()); link.Flush(); int32 code; diff --git a/src/kits/app/link_message.h b/src/kits/app/link_message.h index c712bfa131..dc7f7ed59f 100644 --- a/src/kits/app/link_message.h +++ b/src/kits/app/link_message.h @@ -1,3 +1,10 @@ +/* + * Copyright 2005, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler + */ #ifndef _LINK_MESSAGE_H_ #define _LINK_MESSAGE_H_ @@ -5,10 +12,18 @@ #include +static const int32 kLinkCode = '_PTL'; + +static const size_t kInitialBufferSize = 2048; +static const size_t kMaxBufferSize = 65536; + // anything beyond that should be sent with a different mechanism + struct message_header { int32 size; uint32 code; uint32 flags; }; +static const uint32 kNeedsReply = 0x01; + #endif /* _LINK_MESSAGE_H_ */