Files
haiku-beta6/src/kits/app/LinkReceiver.cpp
T

339 lines
7.1 KiB
C++
Raw Normal View History

2005-06-07 14:30:50 +00:00
/*
* Copyright 2001-2005, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pahtz <[email protected]>
* Axel Dörfler
*/
/** Class for low-overhead port-based messaging */
#include <stdlib.h>
#include <string.h>
#include <new>
#include <ServerProtocol.h>
#include <LinkMsgReader.h>
2005-06-07 14:30:50 +00:00
#include "link_message.h"
//#define DEBUG_BPORTLINK
#ifdef DEBUG_BPORTLINK
# include <stdio.h>
# define STRACE(x) printf x
2005-06-07 14:30:50 +00:00
// those are defined in LinkMsgSender.cpp
extern const char *strcode(int32 code);
extern const char *bstrcode(int32 code);
#else
# define STRACE(x) ;
#endif
namespace BPrivate {
LinkReceiver::LinkReceiver(port_id port)
2005-06-07 14:30:50 +00:00
:
fReceivePort(port), fRecvBuffer(NULL), fRecvPosition(0), fRecvStart(0),
fRecvBufferSize(0), fDataSize(0),
fReplySize(0), fReadError(B_OK)
{
}
2005-06-07 14:30:50 +00:00
LinkReceiver::~LinkReceiver()
{
2005-06-07 14:30:50 +00:00
free(fRecvBuffer);
}
2005-06-07 14:30:50 +00:00
void
LinkReceiver::SetPort(port_id port)
{
2005-06-07 14:30:50 +00:00
fReceivePort = port;
}
2005-06-07 14:30:50 +00:00
status_t
LinkReceiver::GetNextMessage(int32 &code, bigtime_t timeout)
{
int32 remaining;
fReadError = B_OK;
remaining = fDataSize - (fRecvStart + fReplySize);
STRACE(("info: LinkReceiver GetNextReply() reports %ld bytes remaining in buffer.\n", remaining));
2005-06-07 14:30:50 +00:00
// find the position of the next message header in the buffer
message_header *header;
if (remaining <= 0) {
status_t err = ReadFromPort(timeout);
if (err < B_OK)
return err;
remaining = fDataSize;
2005-06-07 14:30:50 +00:00
header = (message_header *)fRecvBuffer;
} else {
fRecvStart += fReplySize; // start of the next message
fRecvPosition = fRecvStart;
2005-06-07 14:30:50 +00:00
header = (message_header *)(fRecvBuffer + fRecvStart);
}
2005-06-07 14:30:50 +00:00
// check we have a well-formed message
if (remaining < (int32)sizeof(message_header)) {
// we don't have enough data for a complete header
STRACE(("error info: LinkReceiver remaining %ld bytes is less than header size.\n", remaining));
ResetBuffer();
return B_ERROR;
}
2005-06-07 14:30:50 +00:00
fReplySize = header->size;
if (fReplySize > remaining || fReplySize < (int32)sizeof(message_header)) {
STRACE(("error info: LinkReceiver message size of %ld bytes smaller than header size.\n", fReplySize));
ResetBuffer();
return B_ERROR;
}
code = header->code;
2005-06-07 14:30:50 +00:00
fRecvPosition += sizeof(message_header);
STRACE(("info: LinkReceiver got header %s [%ld %ld %ld] from port %ld.\n",
2005-06-07 14:30:50 +00:00
strcode(header->code), fReplySize, header->code, header->flags, fReceivePort));
return B_OK;
}
2005-06-07 14:30:50 +00:00
bool
LinkReceiver::NeedsReply() const
{
if (fReplySize == 0)
return false;
message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
return (header->flags & kNeedsReply) != 0;
}
int32
LinkReceiver::Code() const
{
if (fReplySize == 0)
return B_ERROR;
message_header *header = (message_header *)(fRecvBuffer + fRecvStart);
return header->code;
}
2005-06-07 14:30:50 +00:00
void
LinkReceiver::ResetBuffer()
{
fRecvPosition = 0;
fRecvStart = 0;
fDataSize = 0;
fReplySize = 0;
}
2005-06-07 14:30:50 +00:00
status_t
LinkReceiver::AdjustReplyBuffer(bigtime_t timeout)
{
2005-06-07 14:30:50 +00:00
// Here we take advantage of the compiler's dead-code elimination
if (kInitialBufferSize == kMaxBufferSize) {
2005-06-07 14:30:50 +00:00
// fixed buffer size
if (fRecvBuffer != NULL)
return B_OK;
fRecvBuffer = (char *)malloc(kInitialBufferSize);
if (fRecvBuffer == NULL)
return B_NO_MEMORY;
fRecvBufferSize = kInitialBufferSize;
2005-06-07 14:30:50 +00:00
} else {
STRACE(("info: LinkReceiver getting port_buffer_size().\n"));
ssize_t bufferSize;
if (timeout == B_INFINITE_TIMEOUT)
bufferSize = port_buffer_size(fReceivePort);
else
bufferSize = port_buffer_size_etc(fReceivePort, B_TIMEOUT, timeout);
STRACE(("info: LinkReceiver got port_buffer_size() = %ld.\n", bufferSize));
if (bufferSize < 0)
return (status_t)bufferSize;
2005-06-07 14:30:50 +00:00
// make sure our receive buffer is large enough
if (bufferSize > fRecvBufferSize) {
if (bufferSize <= (ssize_t)kInitialBufferSize)
bufferSize = (ssize_t)kInitialBufferSize;
else
2005-06-24 02:44:49 +00:00
bufferSize = (bufferSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
if (bufferSize > (ssize_t)kMaxBufferSize)
return B_ERROR; // we can't continue
STRACE(("info: LinkReceiver setting receive buffersize to %ld.\n", bufferSize));
char *buffer = (char *)malloc(bufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
2005-06-07 14:30:50 +00:00
free(fRecvBuffer);
fRecvBuffer = buffer;
fRecvBufferSize = bufferSize;
}
}
return B_OK;
}
2005-06-07 14:30:50 +00:00
status_t
LinkReceiver::ReadFromPort(bigtime_t timeout)
{
2005-06-07 14:30:50 +00:00
// we are here so it means we finished reading the buffer contents
ResetBuffer();
status_t err = AdjustReplyBuffer(timeout);
if (err < B_OK)
return err;
int32 code;
ssize_t bytesRead;
STRACE(("info: LinkReceiver reading port %ld.\n", fReceivePort));
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: LinkReceiver 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;
}
fDataSize = bytesRead;
return B_OK;
}
2005-06-07 14:30:50 +00:00
status_t
LinkReceiver::Read(void *data, ssize_t size)
{
// STRACE(("info: LinkReceiver Read()ing %ld bytes...\n", size));
if (fReadError < B_OK)
return fReadError;
2005-06-07 14:30:50 +00:00
if (size < 1) {
fReadError = B_BAD_VALUE;
return B_BAD_VALUE;
}
if (fDataSize == 0 || fReplySize == 0)
2005-06-07 14:30:50 +00:00
return B_NO_INIT; // need to call GetNextReply() first
2005-06-07 14:30:50 +00:00
if (fRecvPosition + size > fRecvStart + fReplySize) {
// reading past the end of current message
fReadError = B_BAD_VALUE;
return B_BAD_VALUE;
}
memcpy(data, fRecvBuffer + fRecvPosition, size);
fRecvPosition += size;
return fReadError;
}
2005-06-07 14:30:50 +00:00
status_t
LinkReceiver::ReadString(char **_string)
{
int32 length = 0;
status_t status;
status = Read<int32>(&length);
if (status < B_OK)
return status;
if (length >= 0) {
char *string = (char *)malloc(length + 1);
if (string == NULL) {
fRecvPosition -= sizeof(int32); // rewind the transaction
return B_NO_MEMORY;
}
if (length > 0) {
status = Read(string, length);
if (status < B_OK) {
free(string);
fRecvPosition -= sizeof(int32); // rewind the transaction
return status;
}
}
// make sure the string is null terminated
string[length] = '\0';
*_string = string;
return B_OK;
2005-06-07 14:30:50 +00:00
} else {
fRecvPosition -= sizeof(int32); // rewind the transaction
return B_ERROR;
}
}
status_t
LinkReceiver::ReadString(char *buffer, size_t bufferLength)
{
int32 length = 0;
status_t status;
status = Read<int32>(&length);
if (status < B_OK)
return status;
if (length >= (int32)bufferLength) {
status = B_BUFFER_OVERFLOW;
goto err;
}
if (length < 0) {
status = B_ERROR;
goto err;
}
if (length > 0) {
status = Read(buffer, length);
if (status < B_OK)
goto err;
}
// make sure the string is null terminated
buffer[length] = '\0';
return B_OK;
err:
fRecvPosition -= sizeof(int32);
// rewind the transaction
return status;
}
} // namespace BPrivate