* Added per-port sanity limits. We limit the number and summed size of

messages.
* We now maintain a second message list per port, which is sorted by
  timeout time. Thus we can drop timed out messages as early as possible.
* Fixed a bug which caused messages to disappear in the port list.
* Now delete a port not only when an error occurred when delivering a
  message, but also when it is empty.
* More debug output.

Seems to be working well now.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@11142 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2005-01-30 00:54:59 +00:00
parent 0353d33137
commit c6dbc50e2b
2 changed files with 152 additions and 37 deletions
+151 -37
View File
@@ -5,10 +5,12 @@
#include <map> #include <map>
#include <new> #include <new>
#include <set>
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <Autolock.h> #include <Autolock.h>
#include <DataIO.h> #include <DataIO.h>
#include <DoublyLinkedList2.h>
#include <MessagePrivate.h> #include <MessagePrivate.h>
#include <MessengerPrivate.h> #include <MessengerPrivate.h>
#include <OS.h> #include <OS.h>
@@ -16,13 +18,18 @@
#include <messaging.h> #include <messaging.h>
#include "Debug.h"
#include "MessageDeliverer.h" #include "MessageDeliverer.h"
#include "Referenceable.h" #include "Referenceable.h"
// sDeliverer -- the singleton instance // sDeliverer -- the singleton instance
MessageDeliverer *MessageDeliverer::sDeliverer = NULL; MessageDeliverer *MessageDeliverer::sDeliverer = NULL;
static const bigtime_t kRetryDelay = 20000; // 20 ms static const bigtime_t kRetryDelay = 100000; // 100 ms
// per port sanity limits
static const int32 kMaxMessagesPerPort = 10000;
static const int32 kMaxDataPerPort = 50 * 1024 * 1024; // 50 MB
// Message // Message
class MessageDeliverer::Message : public Referenceable { class MessageDeliverer::Message : public Referenceable {
@@ -67,6 +74,11 @@ public:
return fTimeoutTime; return fTimeoutTime;
} }
bool HasTimeout() const
{
return (fTimeoutTime < B_INFINITE_TIMEOUT);
}
void SetBusy(bool busy) void SetBusy(bool busy)
{ {
fBusy = busy; fBusy = busy;
@@ -86,12 +98,12 @@ private:
}; };
// TargetMessage // TargetMessage
class MessageDeliverer::TargetMessage { class MessageDeliverer::TargetMessage
: public DoublyLinkedListLinkImpl<MessageDeliverer::TargetMessage> {
public: public:
TargetMessage(Message *message, int32 token) TargetMessage(Message *message, int32 token)
: fMessage(message), : fMessage(message),
fToken(token), fToken(token)
fNext(NULL)
{ {
if (fMessage) if (fMessage)
fMessage->AddReference(); fMessage->AddReference();
@@ -113,20 +125,58 @@ public:
return fToken; return fToken;
} }
void SetNext(TargetMessage *next) private:
Message *fMessage;
int32 fToken;
};
// TargetMessageHandle
class MessageDeliverer::TargetMessageHandle {
public:
TargetMessageHandle(TargetMessage *message)
: fMessage(message)
{ {
fNext = next;
} }
TargetMessage *Next() const TargetMessageHandle(const TargetMessageHandle &other)
: fMessage(other.fMessage)
{ {
return fNext; }
TargetMessage *GetMessage() const
{
return fMessage;
}
TargetMessageHandle &operator=(const TargetMessageHandle &other)
{
fMessage = other.fMessage;
return *this;
}
bool operator==(const TargetMessageHandle &other) const
{
return (fMessage == other.fMessage);
}
bool operator!=(const TargetMessageHandle &other) const
{
return (fMessage != other.fMessage);
}
bool operator<(const TargetMessageHandle &other) const
{
bigtime_t timeout = fMessage->GetMessage()->TimeoutTime();
bigtime_t otherTimeout = other.fMessage->GetMessage()->TimeoutTime();
if (timeout < otherTimeout)
return true;
if (timeout > otherTimeout)
return false;
return (fMessage < other.fMessage);
} }
private: private:
Message *fMessage; TargetMessage *fMessage;
int32 fToken;
TargetMessage *fNext;
}; };
// TargetPort // TargetPort
@@ -134,14 +184,15 @@ class MessageDeliverer::TargetPort {
public: public:
TargetPort(port_id portID) TargetPort(port_id portID)
: fPortID(portID), : fPortID(portID),
fFirstMessage(NULL), fMessages(),
fLastMessage(NULL) fMessageCount(0),
fMessageSize(0)
{ {
} }
~TargetPort() ~TargetPort()
{ {
while (fFirstMessage) while (!fMessages.IsEmpty())
PopMessage(); PopMessage();
} }
@@ -152,6 +203,8 @@ public:
status_t PushMessage(Message *message, int32 token) status_t PushMessage(Message *message, int32 token)
{ {
PRINT(("MessageDeliverer::TargetPort::PushMessage(port: %ld, %p, %ld)\n",
fPortID, message, token));
// create a target message // create a target message
TargetMessage *targetMessage TargetMessage *targetMessage
= new(nothrow) TargetMessage(message, token); = new(nothrow) TargetMessage(message, token);
@@ -159,49 +212,103 @@ public:
return B_NO_MEMORY; return B_NO_MEMORY;
// push it // push it
if (fLastMessage) fMessages.Insert(targetMessage);
fLastMessage->SetNext(targetMessage); fMessageCount++;
else fMessageSize += targetMessage->GetMessage()->DataSize();
fFirstMessage = fLastMessage = targetMessage;
// add it to the timeoutable messages, if it has a timeout
if (message->HasTimeout())
fTimeoutableMessages.insert(targetMessage);
_EnforceLimits();
return B_OK; return B_OK;
} }
Message *PeekMessage(int32 &token) const Message *PeekMessage(int32 &token) const
{ {
if (!fFirstMessage) if (!fMessages.Head())
return NULL; return NULL;
token = fFirstMessage->Token(); token = fMessages.Head()->Token();
return fFirstMessage->GetMessage(); return fMessages.Head()->GetMessage();
} }
void PopMessage() void PopMessage()
{ {
if (fFirstMessage) { if (fMessages.Head()) {
TargetMessage *message = fFirstMessage; PRINT(("MessageDeliverer::TargetPort::PopMessage(): port: %ld, %p\n",
fFirstMessage = message->Next(); fPortID, fMessages.Head()->GetMessage()));
if (!fFirstMessage) _RemoveMessage(fMessages.Head());
fLastMessage = NULL; }
delete message; }
void DropTimedOutMessages()
{
bigtime_t now = system_time();
while (fTimeoutableMessages.begin() != fTimeoutableMessages.end()) {
TargetMessage *message = fTimeoutableMessages.begin()->GetMessage();
if (message->GetMessage()->TimeoutTime() > now)
break;
PRINT(("MessageDeliverer::TargetPort::DropTimedOutMessages(): port: %ld: "
"message %p timed out\n", fPortID, message->GetMessage()));
_RemoveMessage(message);
} }
} }
bool IsEmpty() const bool IsEmpty() const
{ {
return !fFirstMessage; return fMessages.IsEmpty();
} }
private: private:
port_id fPortID; void _RemoveMessage(TargetMessage *message)
TargetMessage *fFirstMessage; {
TargetMessage *fLastMessage; fMessages.Remove(message);
fMessageCount--;
fMessageSize -= message->GetMessage()->DataSize();
if (message->GetMessage()->HasTimeout())
fTimeoutableMessages.erase(message);
delete message;
}
void _EnforceLimits()
{
// message count
while (fMessageCount > kMaxMessagesPerPort) {
PRINT(("MessageDeliverer::TargetPort::_EnforceLimits(): port: %ld: hit maximum "
"message count limit.\n", fPortID));
PopMessage();
}
// message size
while (fMessageSize > kMaxDataPerPort) {
PRINT(("MessageDeliverer::TargetPort::_EnforceLimits(): port: %ld: hit maximum "
"message size limit.\n", fPortID));
PopMessage();
}
}
typedef DoublyLinkedList<TargetMessage> MessageList;
port_id fPortID;
MessageList fMessages;
int32 fMessageCount;
int32 fMessageSize;
set<TargetMessageHandle> fTimeoutableMessages;
}; };
// TargetPortMap // TargetPortMap
struct MessageDeliverer::TargetPortMap : public map<port_id, TargetPort*> { struct MessageDeliverer::TargetPortMap : public map<port_id, TargetPort*> {
}; };
// #pragma mark -
// constructor // constructor
MessageDeliverer::MessageDeliverer() MessageDeliverer::MessageDeliverer()
: fLock("message deliverer"), : fLock("message deliverer"),
@@ -481,8 +588,11 @@ MessageDeliverer::_PutTargetPort(TargetPort *port)
status_t status_t
MessageDeliverer::_SendMessage(Message *message, port_id portID, int32 token) MessageDeliverer::_SendMessage(Message *message, port_id portID, int32 token)
{ {
return BMessage::Private::SendFlattenedMessage(message->Data(), status_t error = BMessage::Private::SendFlattenedMessage(message->Data(),
message->DataSize(), portID, token, (token < 0), 0); message->DataSize(), portID, token, (token < 0), 0);
//PRINT(("MessageDeliverer::_SendMessage(%p, port: %ld, token: %ld): %lx\n",
//message, portID, token, error));
return error;
} }
// _DelivererThreadEntry // _DelivererThreadEntry
@@ -508,15 +618,19 @@ MessageDeliverer::_DelivererThread()
TargetPort *port = it->second; TargetPort *port = it->second;
bool portError = false; bool portError = false;
port->DropTimedOutMessages();
// try sending all messages // try sending all messages
int32 token; int32 token;
while (Message *message = port->PeekMessage(token)) { while (Message *message = port->PeekMessage(token)) {
status_t error = B_OK; status_t error = B_OK;
if (message->TimeoutTime() > system_time()) { // if (message->TimeoutTime() > system_time()) {
error = _SendMessage(message, port->PortID(), token); error = _SendMessage(message, port->PortID(), token);
} else { // } else {
// timeout, drop message // // timeout, drop message
} // PRINT(("MessageDeliverer::_DelivererThread(): port %ld, "
// "message %p timed out\n", port->PortID(), message));
// }
if (error == B_OK) { if (error == B_OK) {
port->PopMessage(); port->PopMessage();
@@ -530,7 +644,7 @@ MessageDeliverer::_DelivererThread()
} }
// next port // next port
if (portError) { if (portError || port->IsEmpty()) {
TargetPortMap::iterator oldIt = it; TargetPortMap::iterator oldIt = it;
++it; ++it;
delete port; delete port;
+1
View File
@@ -42,6 +42,7 @@ public:
private: private:
class Message; class Message;
class TargetMessage; class TargetMessage;
class TargetMessageHandle;
class TargetPort; class TargetPort;
struct TargetPortMap; struct TargetPortMap;