/* * 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 #endif #ifdef HAVE_SHARE_H #include #endif // Is this process currently signaled? bool StdSchedulerProc::IsSignaled() { // Initialize file descriptor sets std::vector 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 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 & checkfds) { pollfd pfd = { fds[0], POLLIN, 0 }; checkfds.push_back(pfd); } bool StdScheduler::DoScheduleProcs(int iTimeout) { // Initialize file descriptor sets std::vector fds; std::map > 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(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 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 & 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