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; }