Files
secondo/Algebras/Pregel/MessageBroker/MessageServer.cpp
2026-01-23 17:03:45 +08:00

290 lines
7.7 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2004, University in Hagen, Department of Computer Science,
Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
//paragraph [1] Title: [{\Large \bf \begin {center}] [\end {center}}]
//[TOC] [\tableofcontents]
[1] Header File of the class ~PregelAlgebra~
November 2018, J. Mende
[TOC]
1 Overview
This file defines the members of class MessageServer
*/
#include <iostream>
#include "MessageServer.h"
#include <boost/log/trivial.hpp>
#include "../Helpers/Metrics.h"
#include "../PregelContext.h"
namespace pregel {
MessageServer::MessageServer(std::shared_ptr<Socket> _socket,
executable _initDoneCallback) :
stateCondition(),
initDoneCallback(_initDoneCallback),
socket(_socket) {
thread = new boost::thread(boost::bind(&MessageServer::run, this));
}
MessageServer::~MessageServer() {
socket->GetSocketStream().setstate(std::ios_base::failbit);
socket->Close(); //TODO: need to do more?
if (!thread->timed_join(boost::posix_time::milliseconds(0))) {
thread->interrupt();
if(thread->joinable()){
thread->join();
}
}
delete thread;
}
void MessageServer::run() {
setState(WAITING, false);
waitToResumeReading();
try {
while (!boost::this_thread::interruption_requested()) {
processMessage();
}
} catch (boost::thread_interrupted &e) {
BOOST_LOG_TRIVIAL(error) << "catching interrupted";
return;
}
}
void MessageServer::interrupt() {
thread->interrupt();
}
void MessageServer::processMessage() {
boost::this_thread::interruption_point();
if(!socket->IsOk()) {
interrupt();
return;
}
char headerBuffer[MessageWrapper::HEADER_SIZE];
memset(headerBuffer,0,MessageWrapper::HEADER_SIZE);
std::string bufferToString(headerBuffer, MessageWrapper::HEADER_SIZE);
size_t lengthRead = 0;
char* offset = headerBuffer;
while (lengthRead < MessageWrapper::HEADER_SIZE) {
if (!stateIs(READING)) {
return;
}
lengthRead = socket->Read((void *) offset, MessageWrapper::HEADER_SIZE,
MessageWrapper::HEADER_SIZE,
1);
if (lengthRead < 0) {
// problem during reading data from socket
interrupt();
return;
}
if (lengthRead < MessageWrapper::HEADER_SIZE && lengthRead > 0) {
BOOST_LOG_TRIVIAL(warning) << "header partially received (Read "
<< lengthRead << "B / "
<< MessageWrapper::HEADER_SIZE << "B)";
BOOST_LOG_TRIVIAL(warning) << "buffer before: " << bufferToString.c_str();
BOOST_LOG_TRIVIAL(warning) << "buffer after: " <<
std::string(headerBuffer,
MessageWrapper::HEADER_SIZE).c_str();
}
offset += lengthRead;
}
if(!socket->IsOk()) {
interrupt();
return;
}
auto header = MessageWrapper::Header::read(headerBuffer);
const MessageWrapper::MessageType messageType = header.type;
switch (messageType) {
case MessageWrapper::MessageType::EMPTY:
handleEmptyMessage();
return;
case MessageWrapper::MessageType::FINISH:
handleFinishedMessage();
return;
case MessageWrapper::MessageType::INIT_DONE:
handleInitDoneMessage();
return;
case MessageWrapper::MessageType::DATA:
RECEIVED_MESSAGE
break;
default:
// BOOST_LOG_TRIVIAL(error) << "Received message of unknown type";
assert(false);
return;
}
const unsigned long bodySize = header.length;
char* bodyBuffer = new char[bodySize];
socket->Read((void *) bodyBuffer,
bodySize); //blocking, but awaiting message body already
auto message = MessageWrapper::deserialize(bodyBuffer, header);
delete[] bodyBuffer;
addMessage(message);
}
void MessageServer::addMessage(std::shared_ptr<MessageWrapper> message) {
messageQueue.push(message, message->getRound());
}
void MessageServer::handleEmptyMessage() {
boost::unique_lock<boost::mutex> lock(monitormtx);
while(monitor==nullptr){
monitorCond.wait(lock);
}
{
boost::lock_guard<boost::mutex> lock(stateLock);
state = WAITING;
auto monitorLocal = monitor;
monitor = nullptr;
monitorLocal->empty();
}
// only iff it's the last one to finish,
// it then collects the messages all together
waitToResumeReading();
}
void MessageServer::handleFinishedMessage() {
boost::unique_lock<boost::mutex> lock(monitormtx);
while(monitor==nullptr){
monitorCond.wait(lock);
}
{
boost::lock_guard<boost::mutex> lock(stateLock);
this->state = WAITING;
auto monitorLocal = monitor;
monitor = nullptr;
monitorLocal->finish();
}
// only iff it's the last one to finish,
// it also collects the messages all together
waitToResumeReading();
}
void MessageServer::handleInitDoneMessage() {
if (monitor != nullptr) {
boost::lock_guard<boost::mutex> lock(stateLock);
auto monitorLocal = monitor;
monitor = nullptr;
monitorLocal->finish();
}
setState(WAITING, false);
bringAllMessagesToRound(SuperstepCounter::get());
initDoneCallback();
}
void MessageServer::bringAllMessagesToRound(const int round) {
messageQueue.bringMessagesToRound(round);
}
void MessageServer::drainBuffer(const consumer2<MessageWrapper> &consumer,
const int round) {
messageQueue.consume(consumer, round);
}
void MessageServer::waitToResumeReading() {
boost::unique_lock<boost::mutex> lock(stateLock);
stateCondition.wait(lock, [&]() {
return state == READING || boost::this_thread::interruption_requested();
});
}
void MessageServer::startReading() {
setState(READING);
}
void MessageServer::requestPause() {
setState(WAITING);
}
bool MessageServer::stateIs(MessageServer::State state) {
boost::lock_guard<boost::mutex> lock(stateLock);
return this->state == state;
}
void MessageServer::setState(MessageServer::State state, bool notify) {
{
boost::lock_guard<boost::mutex> lock(stateLock);
if(this->state == state){
return;
}
this->state = state;
}
if (notify) {
stateCondition.notify_one();
}
}
std::string MessageServer::stateStr()const{
switch(state){
case READING : return "READING";
case WAITING : return "WAITING";
default : return "unknown";
}
}
void MessageServer::healthReport(std::stringstream &sstream) {
boost::lock_guard<boost::mutex> guard(stateLock);
if (socket == nullptr) {
sstream << " socket is nullptr" << std::endl;
return;
}
sstream << " Socket: " << (socket->IsOk() ? "OK" : "NOT ok")
<< std::endl;
sstream << " Address: " << socket->GetSocketAddress().c_str()
<< std::endl;
sstream << " State: " << stateStr() << std::endl;
sstream << " Queue: " << messageQueue << std::endl;
sstream << " Monitor: " << (monitor==nullptr?"NULL":"PRESENT")
<< std::endl;
if(monitor!=nullptr){
monitor->print(sstream, " ");
}
}
void MessageServer::setMonitor(std::shared_ptr<Monitor> monitor) {
this->monitor = monitor;
monitorCond.notify_all();
}
}