NekoX/TMessagesProj/jni/libtgvoip2/MessageThread.cpp

183 lines
4.3 KiB
C++

//
// Created by Grishka on 17.06.2018.
//
#include <assert.h>
#include <time.h>
#include <math.h>
#include <float.h>
#include <stdint.h>
#ifndef _WIN32
#include <sys/time.h>
#endif
#include "MessageThread.h"
#include "VoIPController.h"
#include "logging.h"
using namespace tgvoip;
MessageThread::MessageThread() : Thread(std::bind(&MessageThread::Run, this)){
running=true;
SetName("MessageThread");
#ifdef _WIN32
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
event=CreateEvent(NULL, false, false, NULL);
#else
event=CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
#endif
#else
pthread_cond_init(&cond, NULL);
#endif
}
MessageThread::~MessageThread(){
Stop();
#ifdef _WIN32
CloseHandle(event);
#else
pthread_cond_destroy(&cond);
#endif
}
void MessageThread::Stop(){
if(running){
running=false;
#ifdef _WIN32
SetEvent(event);
#else
pthread_cond_signal(&cond);
#endif
Join();
}
}
void MessageThread::Run(){
queueMutex.Lock();
while(running){
double currentTime=VoIPController::GetCurrentTime();
double waitTimeout;
{
MutexGuard _m(queueAccessMutex);
waitTimeout=queue.empty() ? DBL_MAX : (queue[0].deliverAt-currentTime);
}
//LOGW("MessageThread wait timeout %f", waitTimeout);
if(waitTimeout>0.0){
#ifdef _WIN32
queueMutex.Unlock();
DWORD actualWaitTimeout=waitTimeout==DBL_MAX ? INFINITE : ((DWORD)round(waitTimeout*1000.0));
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
WaitForSingleObject(event, actualWaitTimeout);
#else
WaitForSingleObjectEx(event, actualWaitTimeout, false);
#endif
// we don't really care if a context switch happens here and anything gets added to the queue by another thread
// since any new no-delay messages will get delivered on this iteration anyway
queueMutex.Lock();
#else
if(waitTimeout!=DBL_MAX){
struct timeval now;
struct timespec timeout;
gettimeofday(&now, NULL);
waitTimeout+=now.tv_sec;
waitTimeout+=(now.tv_usec/1000000.0);
timeout.tv_sec=(time_t)(floor(waitTimeout));
timeout.tv_nsec=(long)((waitTimeout-floor(waitTimeout))*1000000000.0);
pthread_cond_timedwait(&cond, queueMutex.NativeHandle(), &timeout);
}else{
pthread_cond_wait(&cond, queueMutex.NativeHandle());
}
#endif
}
if(!running){
queueMutex.Unlock();
return;
}
currentTime=VoIPController::GetCurrentTime();
std::vector<Message> msgsToDeliverNow;
{
MutexGuard _m(queueAccessMutex);
for(std::vector<Message>::iterator m=queue.begin();m!=queue.end();){
if(m->deliverAt==0.0 || currentTime>=m->deliverAt){
msgsToDeliverNow.push_back(*m);
m=queue.erase(m);
continue;
}
++m;
}
}
for(Message& m:msgsToDeliverNow){
//LOGI("MessageThread delivering %u", m.msg);
cancelCurrent=false;
if(m.deliverAt==0.0)
m.deliverAt=VoIPController::GetCurrentTime();
if(m.func!=nullptr){
m.func();
}
if(!cancelCurrent && m.interval>0.0){
m.deliverAt+=m.interval;
InsertMessageInternal(m);
}
}
}
queueMutex.Unlock();
}
uint32_t MessageThread::Post(std::function<void()> func, double delay, double interval){
assert(delay>=0);
//LOGI("MessageThread post [function] delay %f", delay);
double currentTime=VoIPController::GetCurrentTime();
Message m{lastMessageID++, delay==0.0 ? 0.0 : (currentTime+delay), interval, func};
InsertMessageInternal(m);
if(!IsCurrent()){
#ifdef _WIN32
SetEvent(event);
#else
pthread_cond_signal(&cond);
#endif
}
return m.id;
}
void MessageThread::InsertMessageInternal(MessageThread::Message &m){
MutexGuard _m(queueAccessMutex);
if(queue.empty()){
queue.push_back(m);
}else{
if(queue[0].deliverAt>m.deliverAt){
queue.insert(queue.begin(), m);
}else{
std::vector<Message>::iterator insertAfter=queue.begin();
for(; insertAfter!=queue.end(); ++insertAfter){
std::vector<Message>::iterator next=std::next(insertAfter);
if(next==queue.end() || (next->deliverAt>m.deliverAt && insertAfter->deliverAt<=m.deliverAt)){
queue.insert(next, m);
break;
}
}
}
}
}
void MessageThread::Cancel(uint32_t id){
MutexGuard _m(queueAccessMutex);
for(std::vector<Message>::iterator m=queue.begin();m!=queue.end();){
if(m->id==id){
m=queue.erase(m);
}else{
++m;
}
}
}
void MessageThread::CancelSelf(){
assert(IsCurrent());
cancelCurrent=true;
}