Threading changes for the new loop architecture

- threading::Manager is no longer an IOSource.
- threading::MsgThread is now an IOSource. This allows threads themselves to signal when they have data to process instead of continually checking each of the threads on every loop pass.
- Make the thread heartbeat timer an actual timer and let it fire as necessary instead of checking to see if it should fire
This commit is contained in:
Tim Wojtulewicz 2019-11-26 12:54:51 -07:00
parent a159d075cf
commit 8b9160fb7e
6 changed files with 141 additions and 66 deletions

View file

@ -1,33 +1,44 @@
#include <sys/socket.h>
#include <unistd.h>
#include "Manager.h"
#include "NetVar.h"
#include "iosource/Manager.h"
using namespace threading;
void HeartbeatTimer::Dispatch(double t, int is_expire)
{
if ( is_expire )
return;
thread_mgr->SendHeartbeats();
thread_mgr->StartHeartbeatTimer();
}
Manager::Manager()
{
DBG_LOG(DBG_THREADING, "Creating thread manager ...");
did_process = true;
next_beat = 0;
terminating = false;
SetIdle(true);
}
Manager::~Manager()
{
if ( all_threads.size() )
Terminate();
if ( heartbeat_timer )
delete heartbeat_timer;
}
void Manager::Terminate()
{
DBG_LOG(DBG_THREADING, "Terminating thread manager ...");
terminating = true;
// First process remaining thread output for the message threads.
do Process(); while ( did_process );
do Flush(); while ( did_process );
// Signal all to stop.
@ -46,17 +57,15 @@ void Manager::Terminate()
all_threads.clear();
msg_threads.clear();
SetIdle(true);
SetClosed(true);
terminating = false;
}
void Manager::AddThread(BasicThread* thread)
{
DBG_LOG(DBG_THREADING, "Adding thread %s ...", thread->Name());
all_threads.push_back(thread);
SetIdle(false);
if ( ! heartbeat_timer )
StartHeartbeatTimer();
}
void Manager::AddMsgThread(MsgThread* thread)
@ -65,34 +74,6 @@ void Manager::AddMsgThread(MsgThread* thread)
msg_threads.push_back(thread);
}
void Manager::GetFds(iosource::FD_Set* read, iosource::FD_Set* write,
iosource::FD_Set* except)
{
}
double Manager::NextTimestamp(double* network_time)
{
// fprintf(stderr, "N %.6f %.6f did_process=%d next_next=%.6f\n", ::network_time, timer_mgr->Time(), (int)did_process, next_beat);
if ( ::network_time && (did_process || ::network_time > next_beat || ! next_beat) )
// If we had something to process last time (or out heartbeat
// is due or not set yet), we want to check for more asap.
return timer_mgr->Time();
for ( msg_thread_list::iterator i = msg_threads.begin(); i != msg_threads.end(); i++ )
{
MsgThread* t = *i;
if ( t->MightHaveOut() || t->Killed() )
// Even if the thread doesn't have output, it may be killed/done,
// which should also signify that processing is needed. The
// "processing" in that case is joining the thread and deleting it.
return timer_mgr->Time();
}
return -1.0;
}
void Manager::KillThreads()
{
DBG_LOG(DBG_THREADING, "Killing threads ...");
@ -107,7 +88,46 @@ void Manager::KillThread(BasicThread* thread)
thread->Kill();
}
void Manager::Process()
void Manager::SendHeartbeats()
{
for ( MsgThread* thread : msg_threads )
thread->Heartbeat();
// Since this is a regular timer, this is also an ideal place to check whether we have
// and dead threads and to delete them.
all_thread_list to_delete;
for ( all_thread_list::iterator i = all_threads.begin(); i != all_threads.end(); i++ )
{
BasicThread* t = *i;
if ( t->Killed() )
to_delete.push_back(t);
}
for ( all_thread_list::iterator i = to_delete.begin(); i != to_delete.end(); i++ )
{
BasicThread* t = *i;
t->WaitForStop();
all_threads.remove(t);
MsgThread* mt = dynamic_cast<MsgThread *>(t);
if ( mt )
msg_threads.remove(mt);
t->Join();
delete t;
}
}
void Manager::StartHeartbeatTimer()
{
heartbeat_timer = new HeartbeatTimer(network_time + BifConst::Threading::heartbeat_interval);
timer_mgr->Add(heartbeat_timer);
}
void Manager::Flush()
{
bool do_beat = false;
@ -192,5 +212,3 @@ const threading::Manager::msg_stats_list& threading::Manager::GetMsgThreadStats(
return stats;
}