forked from Mirrors/openclonk
241 lines
5.3 KiB
C++
241 lines
5.3 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
|
|
#include "C4Include.h"
|
|
#include "platform/StdScheduler.h"
|
|
|
|
#ifdef HAVE_POLL_H
|
|
#ifdef HAVE_IO_H
|
|
#include <io.h>
|
|
#endif
|
|
#ifdef HAVE_SHARE_H
|
|
#include <share.h>
|
|
#endif
|
|
|
|
// Is this process currently signaled?
|
|
bool StdSchedulerProc::IsSignaled()
|
|
{
|
|
// Initialize file descriptor sets
|
|
std::vector<struct pollfd> fds;
|
|
|
|
// Get file descriptors
|
|
GetFDs(fds);
|
|
|
|
// Test
|
|
return poll(&fds[0], fds.size(), 0) > 0;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void Fail(const char* msg)
|
|
{
|
|
Log(msg);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_SYS_EVENTFD_H
|
|
#include <sys/eventfd.h>
|
|
|
|
CStdNotifyProc::CStdNotifyProc()
|
|
{
|
|
fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
|
if (fds[0] == -1)
|
|
Fail("eventfd failed");
|
|
}
|
|
CStdNotifyProc::~CStdNotifyProc()
|
|
{
|
|
close(fds[0]);
|
|
}
|
|
void CStdNotifyProc::Notify()
|
|
{
|
|
uint64_t n = 1;
|
|
if (write(fds[0], &n, 8) == -1)
|
|
Fail("write failed");
|
|
}
|
|
bool CStdNotifyProc::CheckAndReset()
|
|
{
|
|
uint64_t n;
|
|
return (read(fds[0], &n, 8) != -1);
|
|
}
|
|
#else
|
|
CStdNotifyProc::CStdNotifyProc()
|
|
{
|
|
if (pipe(fds) == -1)
|
|
Fail("pipe failed");
|
|
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
|
|
fcntl(fds[0], F_SETFD, FD_CLOEXEC);
|
|
fcntl(fds[1], F_SETFD, FD_CLOEXEC);
|
|
}
|
|
CStdNotifyProc::~CStdNotifyProc()
|
|
{
|
|
close(fds[0]);
|
|
close(fds[1]);
|
|
}
|
|
void CStdNotifyProc::Notify()
|
|
{
|
|
char c = 42;
|
|
if (write(fds[1], &c, 1) == -1)
|
|
Fail("write failed");
|
|
}
|
|
bool CStdNotifyProc::CheckAndReset()
|
|
{
|
|
bool r = false;
|
|
while (1)
|
|
{
|
|
char c;
|
|
if (read(fds[0], &c, 1) <= 0)
|
|
break;
|
|
else
|
|
r = true;
|
|
}
|
|
return r;
|
|
}
|
|
#endif
|
|
void CStdNotifyProc::GetFDs(std::vector<struct pollfd> & checkfds)
|
|
{
|
|
pollfd pfd = { fds[0], POLLIN, 0 };
|
|
checkfds.push_back(pfd);
|
|
}
|
|
|
|
bool StdScheduler::DoScheduleProcs(int iTimeout)
|
|
{
|
|
// Initialize file descriptor sets
|
|
std::vector<struct pollfd> fds;
|
|
std::map<StdSchedulerProc *, std::pair<unsigned int, unsigned int> > fds_for_proc;
|
|
|
|
// Collect file descriptors
|
|
for (auto proc : procs)
|
|
{
|
|
unsigned int os = fds.size();
|
|
proc->GetFDs(fds);
|
|
if (os != fds.size())
|
|
fds_for_proc[proc] = std::pair<unsigned int, unsigned int>(os, fds.size());
|
|
}
|
|
|
|
// Wait for something to happen
|
|
int cnt = poll(&fds[0], fds.size(), iTimeout);
|
|
|
|
bool fSuccess = true;
|
|
|
|
if (cnt >= 0)
|
|
{
|
|
bool any_executed = false;
|
|
auto tNow = C4TimeMilliseconds::Now();
|
|
// Which process?
|
|
for (size_t i = 0; i < procs.size(); i++)
|
|
{
|
|
auto proc = procs[i];
|
|
auto tProcTick = proc->GetNextTick(tNow);
|
|
if (tProcTick <= tNow)
|
|
{
|
|
struct pollfd * pfd = nullptr;
|
|
if (fds_for_proc.find(proc) != fds_for_proc.end())
|
|
pfd = &fds[fds_for_proc[proc].first];
|
|
if (!proc->Execute(0, pfd))
|
|
{
|
|
OnError(proc);
|
|
fSuccess = false;
|
|
}
|
|
any_executed = true;
|
|
continue;
|
|
}
|
|
// no fds?
|
|
if (fds_for_proc.find(proc) == fds_for_proc.end())
|
|
continue;
|
|
// Check intersection
|
|
unsigned int begin = fds_for_proc[proc].first;
|
|
unsigned int end = fds_for_proc[proc].second;
|
|
for (unsigned int j = begin; j < end; ++j)
|
|
{
|
|
if (fds[j].events & fds[j].revents)
|
|
{
|
|
if (any_executed && proc->IsLowPriority())
|
|
break;
|
|
if (!proc->Execute(0, &fds[begin]))
|
|
{
|
|
OnError(proc);
|
|
fSuccess = false;
|
|
}
|
|
any_executed = true;
|
|
// the list of procs might have been changed, but procs must be in both ppProcs and
|
|
// fds_for_proc to be executed, which prevents execution of any proc not polled this round
|
|
// or deleted. Some procs might be skipped or executed twice, but that should be save.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (cnt < 0 && errno != EINTR)
|
|
{
|
|
printf("StdScheduler::%s: poll failed: %s\n",__func__,strerror(errno));
|
|
}
|
|
return fSuccess;
|
|
}
|
|
|
|
#if defined(HAVE_SYS_TIMERFD_H)
|
|
#include <sys/timerfd.h>
|
|
CStdMultimediaTimerProc::CStdMultimediaTimerProc(uint32_t iDelay)
|
|
{
|
|
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
if (fd == -1)
|
|
Log("timerfd_create failed");
|
|
SetDelay(iDelay);
|
|
}
|
|
|
|
CStdMultimediaTimerProc::~CStdMultimediaTimerProc()
|
|
{
|
|
close(fd);
|
|
}
|
|
|
|
void CStdMultimediaTimerProc::SetDelay(uint32_t inDelay)
|
|
{
|
|
struct itimerspec nv, ov;
|
|
nv.it_interval.tv_sec = inDelay / 1000;
|
|
nv.it_interval.tv_nsec = (inDelay % 1000) * 1000000;
|
|
nv.it_value = nv.it_interval;
|
|
timerfd_settime(fd, 0, &nv, &ov);
|
|
}
|
|
|
|
void CStdMultimediaTimerProc::Set()
|
|
{
|
|
struct itimerspec nv, ov;
|
|
timerfd_gettime(fd, &nv);
|
|
nv.it_value.tv_sec = 0;
|
|
nv.it_value.tv_nsec = 1;
|
|
timerfd_settime(fd, 0, &nv, &ov);
|
|
}
|
|
|
|
bool CStdMultimediaTimerProc::CheckAndReset()
|
|
{
|
|
uint64_t n;
|
|
return read(fd, &n, 8) != -1;
|
|
}
|
|
|
|
void CStdMultimediaTimerProc::GetFDs(std::vector<struct pollfd> & checkfds)
|
|
{
|
|
pollfd pfd = { fd, POLLIN, 0 };
|
|
checkfds.push_back(pfd);
|
|
}
|
|
#endif // HAVE_SYS_TIMERFD_H
|
|
|
|
#if !defined(USE_COCOA)
|
|
void StdScheduler::Added(StdSchedulerProc *pProc) {}
|
|
void StdScheduler::Removing(StdSchedulerProc *pProc) {}
|
|
void StdScheduler::Changed(StdSchedulerProc* pProc) {}
|
|
void StdScheduler::StartOnCurrentThread() {}
|
|
#endif
|
|
#endif // HAVE_POLL_H
|