From 8d22950d01ef49a7efff37c56edf3d6d6b72e891 Mon Sep 17 00:00:00 2001
From: DarkWyrm
Date: Mon, 30 Sep 2002 22:34:20 +0000
Subject: [PATCH] API and stability improvements
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@1318 a95241bf-73f2-0310-859d-f6bbb57e9c96
---
.../develop/app/usecases/PortLinkUseCases.htm | 6 +-
headers/private/app/PortLink.h | 31 ++-
src/kits/app/PortLink.cpp | 217 +++++++++++++++---
3 files changed, 204 insertions(+), 50 deletions(-)
diff --git a/docs/develop/app/usecases/PortLinkUseCases.htm b/docs/develop/app/usecases/PortLinkUseCases.htm
index 84bb66a0ef..6c0d358cfc 100644
--- a/docs/develop/app/usecases/PortLinkUseCases.htm
+++ b/docs/develop/app/usecases/PortLinkUseCases.htm
@@ -34,11 +34,11 @@ The document has the following sections:
Creating a message: Creating a message can be as simple as setting the message code (similar to BMessage's what member). Extra data is not required.
-Attaching Data: Adding extra data is as simple as calling the member function Attach(), which makes a copy of the parameter passed to it.
+Attaching Data: Adding extra data is as simple as calling the member function Attach(), which makes a copy of the parameter passed to it. B_ERROR is returned if the no more data can be attached before the message is sent or if the size is invalid. B_NO_MEMORY is returned when the attachments are larger than the target port's capacity.
-Sending a message: Call Flush(). Whatever opcode has been set will be sent to the target. Optionally, a timeout (in microseconds) of type bigtime_t can be specified. This can be useful in preventing deadlocks if the target has crashed and its port fills up.
+Sending a message: Call Flush(). Whatever opcode has been set will be sent to the target. Optionally, a timeout (in microseconds) of type bigtime_t can be specified. This can be useful in preventing deadlocks if the target has crashed and its port fills up. The function returns B_BAD_VALUE if the target port is invalid.
-Synchronous Messaging: This one requires a little more care in order to prevent deadlocks. Attachments may be used as with Flush(), but FlushWithReply() will wait until the target replies unless a timeout value is specified in microseconds of type bigtime_t. A return code of B_ERROR indicates an internal data error and your message is intact. If a reply times out, it will return B_TIMED_OUT. Otherwise, it returns B_OK.
+
Synchronous Messaging: This one requires a little more care in order to prevent deadlocks. Attachments may be used as with Flush(), but FlushWithReply() will wait until the target replies unless a timeout value is specified in microseconds of type bigtime_t. A return code of B_ERROR indicates an internal data error and your message is intact. If a reply times out, it will return B_TIMED_OUT. If the target port is invalid, B_BAD_VALUE is returned. Otherwise, it returns B_OK.
Reply Protocol: The target will receive the message with all attached data with one slight modification to the otherwise chosen message protocol - the first item will be a port_id which is the port to which the sender is to reply. All other attached data (if any) immediately follows this port id.
diff --git a/headers/private/app/PortLink.h b/headers/private/app/PortLink.h
index 9f5432daab..7bff02eab3 100644
--- a/headers/private/app/PortLink.h
+++ b/headers/private/app/PortLink.h
@@ -14,29 +14,40 @@ class PortLinkData;
class PortLink
{
+ typedef struct
+ {
+ int32 code;
+ ssize_t buffersize;
+ int8 *buffer;
+
+ } ReplyData;
public:
PortLink(port_id port);
+ PortLink(const PortLink &link);
~PortLink(void);
void SetOpCode(int32 code);
void SetPort(port_id port);
port_id GetPort(void);
- void Flush(bigtime_t timeout=B_INFINITE_TIMEOUT);
+ status_t Flush(bigtime_t timeout=B_INFINITE_TIMEOUT);
int8* FlushWithReply(int32 *code, status_t *status, ssize_t *buffersize,
bigtime_t timeout=B_INFINITE_TIMEOUT);
- void Attach(void *data, size_t size);
- void Attach(int32 data);
- void Attach(int16 data);
- void Attach(int8 data);
- void Attach(float data);
- void Attach(bool data);
- void Attach(BRect data);
- void Attach(BPoint data);
+ status_t FlushWithReply(PortLink::ReplyData *data,bigtime_t timeout=B_INFINITE_TIMEOUT);
+ status_t Attach(void *data, size_t size);
+ status_t Attach(int32 data);
+ status_t Attach(int16 data);
+ status_t Attach(int8 data);
+ status_t Attach(float data);
+ status_t Attach(bool data);
+ status_t Attach(BRect data);
+ status_t Attach(BPoint data);
void MakeEmpty(void);
protected:
void FlattenData(int8 **buffer,int32 *size);
port_id target, replyport;
- int32 opcode, bufferlength;
+ int32 opcode;
+ uint32 bufferlength,capacity;
int num_attachments;
+ bool port_ok;
PortLinkData *attachments[_PORTLINK_MAX_ATTACHMENTS];
};
diff --git a/src/kits/app/PortLink.cpp b/src/kits/app/PortLink.cpp
index 8fba724569..a3c3904aea 100644
--- a/src/kits/app/PortLink.cpp
+++ b/src/kits/app/PortLink.cpp
@@ -18,6 +18,8 @@ replyport - port used with synchronous messaging - FlushWithReply()
bufferlength - total bytes taken up by attachments
num_attachments - internal variable which is used to track which "slot"
will be the next one to receive an attachment object
+port_ok - flag to signal whether it's ok to send a message
+capacity - contains the storage capacity of the target port.
*/
#include "PortLink.h"
@@ -35,7 +37,7 @@ class PortLinkData
public:
PortLinkData(void);
~PortLinkData(void);
- bool Set(void *data, size_t size);
+ status_t Set(void *data, size_t size);
char *buffer;
size_t buffersize;
};
@@ -45,12 +47,33 @@ PortLink::PortLink(port_id port)
// For this class to be useful (and to prevent a lot of init problems)
// we require a port in the constructor
target=port;
+ port_info pi;
+ port_ok=(get_port_info(target,&pi)==B_OK)?true:false;
+ capacity=pi.capacity;
// We start out without any data attached to the port message
num_attachments=0;
opcode=0;
bufferlength=0;
replyport=create_port(30,"PortLink reply port");
+
+ // TODO: initialize all attachments pointers to NULL
+}
+
+PortLink::PortLink(const PortLink &link)
+{
+ // The copy constructor copies everything except a PortLink's attachments. If there
+ // is some reason why someday I might need to change this behavior, I will, but
+ // for now there is no real reason I can think of.
+ target=link.target;
+ opcode=link.opcode;
+ port_ok=link.port_ok;
+ capacity=link.capacity;
+ bufferlength=0;
+ num_attachments=0;
+ replyport=create_port(30,"PortLink reply port");
+
+ // TODO: initialize all attachments pointers to NULL
}
PortLink::~PortLink(void)
@@ -60,6 +83,9 @@ PortLink::~PortLink(void)
// because the port may no longer be valid in cases such as the app
// is in the process of quitting
MakeEmpty();
+
+ //TODO: Inline the MakeEmpty call in a way such that it ignores num_attachments
+ // and deletes all non-NULL pointers, setting them to NULL afterward.
}
void PortLink::SetOpCode(int32 code)
@@ -74,6 +100,9 @@ void PortLink::SetPort(port_id port)
// Sets the target port. While not necessary in most uses, this exists
// mostly to prevent flexibility problems
target=port;
+ port_info pi;
+ port_ok=(get_port_info(target,&pi)==B_OK)?true:false;
+ capacity=pi.capacity;
}
port_id PortLink::GetPort(void)
@@ -82,13 +111,15 @@ port_id PortLink::GetPort(void)
return target;
}
-void PortLink::Flush(bigtime_t timeout=B_INFINITE_TIMEOUT)
+status_t PortLink::Flush(bigtime_t timeout=B_INFINITE_TIMEOUT)
{
- // Fires a message off to the target, complete with attachments. NOTE:
- // the recipient must delete all attachments, being the PortLink object assumes
- // no responsiblity for the attachments once the message is sent.
+ // Fires a message off to the target, complete with attachments.
int8 *msgbuffer;
int32 size;
+ status_t write_stat=B_OK;
+
+ if(!port_ok)
+ return B_BAD_VALUE;
if(num_attachments>0)
{
@@ -96,27 +127,25 @@ void PortLink::Flush(bigtime_t timeout=B_INFINITE_TIMEOUT)
// Dump message to port, reset attachments, and clean up
if(timeout!=B_INFINITE_TIMEOUT)
- write_port_etc(target,opcode,msgbuffer,size,B_TIMEOUT, timeout);
+ write_stat=write_port_etc(target,opcode,msgbuffer,size,B_TIMEOUT, timeout);
else
- write_port(target,opcode,msgbuffer,size);
+ write_stat=write_port(target,opcode,msgbuffer,size);
MakeEmpty();
}
else
{
if(timeout!=B_INFINITE_TIMEOUT)
- write_port_etc(target,opcode,NULL,0,B_TIMEOUT, timeout);
+ write_stat=write_port_etc(target,opcode,NULL,0,B_TIMEOUT, timeout);
else
- write_port(target,opcode,NULL,0);
+ write_stat=write_port(target,opcode,NULL,0);
}
+ return write_stat;
}
+
int8* PortLink::FlushWithReply(int32 *code, status_t *status, ssize_t *buffersize, bigtime_t timeout=B_INFINITE_TIMEOUT)
{
- // Fires a message to the target and then waits for a reply. The target will
- // receive a message with the first item being the port_id to reply to.
- // NOTE: like Flush(), any attached data must be deleted.
-
- // Effectively, an Attach() call inlined for changes
+ // Deprecated call which functions exactly like PortLink(PortLink::ReplyData *data)
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
{
@@ -124,9 +153,15 @@ int8* PortLink::FlushWithReply(int32 *code, status_t *status, ssize_t *buffersiz
return NULL;
}
+ if(!port_ok)
+ {
+ *status=B_BAD_VALUE;
+ return NULL;
+ }
+
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
- if(pld->Set(&replyport,sizeof(port_id)))
+ if(pld->Set(&replyport,sizeof(port_id))==B_OK)
{
bufferlength+=sizeof(port_id);
}
@@ -187,7 +222,82 @@ int8* PortLink::FlushWithReply(int32 *code, status_t *status, ssize_t *buffersiz
return buffer;
}
-void PortLink::Attach(void *data, size_t size)
+
+// TODO: write test code
+status_t PortLink::FlushWithReply(PortLink::ReplyData *data,bigtime_t timeout=B_INFINITE_TIMEOUT)
+{
+ // Fires a message to the target and then waits for a reply. The target will
+ // receive a message with the first item being the port_id to reply to.
+ // NOTE: like Flush(), any attached data must be deleted.
+
+ // Effectively, an Attach() call inlined for changes
+
+ if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
+ return B_ERROR;
+
+ if(!port_ok)
+ return B_BAD_VALUE;
+
+ // create a new storage object and stash the data
+ PortLinkData *pld=new PortLinkData;
+ if(pld->Set(&replyport,sizeof(port_id)))
+ {
+ bufferlength+=sizeof(port_id);
+ }
+ else
+ {
+ delete pld;
+ return B_ERROR;
+ }
+
+ // Flatten() inlined to make some necessary changes
+ int8 *buffer=new int8[bufferlength];
+ int8 *bufferindex=buffer;
+ size_t size=0;
+
+ // attach our port_id first
+ memcpy(bufferindex, pld->buffer, pld->buffersize);
+ bufferindex += pld->buffersize;
+ size+=pld->buffersize;
+
+ // attach everything else
+ for(int i=0;ibuffer, pld->buffersize);
+ bufferindex += pld->buffersize;
+ size+=pld->buffersize;
+ }
+
+ // Flush the thing....FOOSH! :P
+ write_port(target,opcode,buffer,size);
+ MakeEmpty();
+ delete buffer;
+
+ // Now we wait for the reply
+ if(timeout==B_INFINITE_TIMEOUT)
+ {
+ data->buffersize=port_buffer_size(replyport);
+ if(data->buffersize>0)
+ data->buffer=(int8*)new int8[data->buffersize];
+ read_port(replyport,&(data->code),&(data->buffer), data->buffersize);
+ }
+ else
+ {
+ data->buffersize=port_buffer_size_etc(replyport,0,timeout);
+ if(data->buffersize==B_TIMED_OUT)
+ return B_TIMED_OUT;
+
+ if(data->buffersize>0)
+ data->buffer=(int8*)new int8[data->buffersize];
+ read_port(replyport,&(data->code),&(data->buffer), data->buffersize);
+ }
+
+ // We got this far, so we apparently have some data
+ return B_OK;
+}
+
+status_t PortLink::Attach(void *data, size_t size)
{
// This is the member called to attach data to a message. Attachments are
// treated to be in 'Append' mode, tacking on each attached piece of data
@@ -195,10 +305,13 @@ void PortLink::Attach(void *data, size_t size)
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
if(size==0)
- return;
+ return B_ERROR;
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -212,18 +325,22 @@ void PortLink::Attach(void *data, size_t size)
{
delete pld;
}
+ return B_OK;
}
// These functions were added for a major convenience in passing common types
// Eventually, I'd like to templatize these, but for now, this'll do
-void PortLink::Attach(int32 data)
+status_t PortLink::Attach(int32 data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(int32);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -237,15 +354,19 @@ void PortLink::Attach(int32 data)
{
delete pld;
}
+ return B_OK;
}
-void PortLink::Attach(int16 data)
+status_t PortLink::Attach(int16 data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(int16);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -259,15 +380,19 @@ void PortLink::Attach(int16 data)
{
delete pld;
}
+ return B_OK;
}
-void PortLink::Attach(int8 data)
+status_t PortLink::Attach(int8 data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(int8);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -281,15 +406,19 @@ void PortLink::Attach(int8 data)
{
delete pld;
}
+ return B_OK;
}
-void PortLink::Attach(float data)
+status_t PortLink::Attach(float data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(float);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -303,15 +432,19 @@ void PortLink::Attach(float data)
{
delete pld;
}
+ return B_OK;
}
-void PortLink::Attach(bool data)
+status_t PortLink::Attach(bool data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(bool);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -325,15 +458,19 @@ void PortLink::Attach(bool data)
{
delete pld;
}
+ return B_OK;
}
-void PortLink::Attach(BRect data)
+status_t PortLink::Attach(BRect data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(BRect);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -347,15 +484,19 @@ void PortLink::Attach(BRect data)
{
delete pld;
}
+ return B_OK;
}
-void PortLink::Attach(BPoint data)
+status_t PortLink::Attach(BPoint data)
{
// Prevent parameter problems
if(num_attachments>=_PORTLINK_MAX_ATTACHMENTS)
- return;
+ return B_ERROR;
int32 size=sizeof(BPoint);
+
+ if(bufferlength+size>capacity)
+ return B_NO_MEMORY;
// create a new storage object and stash the data
PortLinkData *pld=new PortLinkData;
@@ -369,6 +510,7 @@ void PortLink::Attach(BPoint data)
{
delete pld;
}
+ return B_OK;
}
void PortLink::FlattenData(int8 **buffer,int32 *size)
@@ -413,26 +555,27 @@ PortLinkData::PortLinkData(void)
{
// Initialize object to empty
buffersize=0;
+ buffer=NULL;
}
PortLinkData::~PortLinkData(void)
{
// Frees the buffer if we actually used the class to store data
- if(buffersize>0)
+ if(buffersize>0 && buffer!=NULL)
free(buffer);
}
-bool PortLinkData::Set(void *data, size_t size)
+status_t PortLinkData::Set(void *data, size_t size)
{
// Function copies the passed to the internal buffers for storage
if(size>0 && buffersize==0 && data!=NULL)
{
buffer=(char *)malloc(size);
if(buffer==NULL)
- return false;
+ return B_NO_MEMORY;
memcpy(buffer, data, size);
buffersize=size;
- return true;
+ return B_OK;
}
- return false;
+ return B_ERROR;
}