| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) RedWolf Design |
| 5 | * Copyright (c) 2017-2021, The LegacyClonk Team and contributors |
| 6 | * |
| 7 | * Distributed under the terms of the ISC license; see accompanying file |
| 8 | * "COPYING" for details. |
| 9 | * |
| 10 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 11 | * See accompanying file "TRADEMARK" for details. |
| 12 | * |
| 13 | * To redistribute this file separately, substitute the full license texts |
| 14 | * for the above references. |
| 15 | */ |
| 16 | |
| 17 | #include "C4Include.h" |
| 18 | #include "C4InteractiveThread.h" |
| 19 | #include "C4Application.h" |
| 20 | #include "C4Log.h" |
| 21 | |
| 22 | #include <C4Game.h> |
| 23 | |
| 24 | #include <cassert> |
| 25 | |
| 26 | // *** C4InteractiveThread |
| 27 | |
| 28 | C4InteractiveThread::C4InteractiveThread() |
| 29 | { |
| 30 | // Add head-item |
| 31 | pFirstEvent = pLastEvent = new Event(); |
| 32 | pFirstEvent->Type = Ev_None; |
| 33 | pFirstEvent->Next = nullptr; |
| 34 | // reset event handlers |
| 35 | std::fill(first: pCallbacks, last: std::end(arr&: pCallbacks), value: nullptr); |
| 36 | } |
| 37 | |
| 38 | C4InteractiveThread::~C4InteractiveThread() |
| 39 | { |
| 40 | CStdLock PushLock(&EventPushCSec), PopLock(&EventPopCSec); |
| 41 | // Remove all items. This may leak data, if pData was allocated on the heap. |
| 42 | while (PopEvent(pEventType: nullptr, data: nullptr)); |
| 43 | // Delete head-item |
| 44 | delete pFirstEvent; |
| 45 | pFirstEvent = pLastEvent = nullptr; |
| 46 | } |
| 47 | |
| 48 | bool C4InteractiveThread::AddProc(StdSchedulerProc *pProc) |
| 49 | { |
| 50 | bool fFirst = !Scheduler.getProcCnt(); |
| 51 | // Add the proc |
| 52 | Scheduler.Add(pProc); |
| 53 | // Not started yet? |
| 54 | if (fFirst) |
| 55 | if (!Scheduler.Start()) |
| 56 | return false; |
| 57 | return true; |
| 58 | } |
| 59 | |
| 60 | void C4InteractiveThread::RemoveProc(StdSchedulerProc *pProc) |
| 61 | { |
| 62 | Scheduler.Remove(pProc); |
| 63 | // Last proc to be removed? |
| 64 | if (Scheduler.getProcCnt() == 0) |
| 65 | { |
| 66 | Scheduler.Stop(); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | bool C4InteractiveThread::PushEvent(C4InteractiveEventType eEvent, std::any data) |
| 71 | { |
| 72 | CStdLock PushLock(&EventPushCSec); |
| 73 | if (!pLastEvent) return false; |
| 74 | // create event |
| 75 | Event *pEvent = new Event; |
| 76 | pEvent->Type = eEvent; |
| 77 | pEvent->Data = std::move(data); |
| 78 | #ifndef NDEBUG |
| 79 | pEvent->Time = timeGetTime(); |
| 80 | #endif |
| 81 | pEvent->Next = nullptr; |
| 82 | // add item (at end) |
| 83 | pLastEvent->Next = pEvent; |
| 84 | pLastEvent = pEvent; |
| 85 | PushLock.Clear(); |
| 86 | #ifdef _WIN32 |
| 87 | // post message to main thread |
| 88 | try |
| 89 | { |
| 90 | Application.NetworkEvent.Set(); |
| 91 | } |
| 92 | catch (const std::runtime_error &) |
| 93 | { |
| 94 | LogFatalNTr("Network: could not post message to main thread!" ); |
| 95 | } |
| 96 | |
| 97 | return true; |
| 98 | #else |
| 99 | return Application.SignalNetworkEvent(); |
| 100 | #endif |
| 101 | } |
| 102 | |
| 103 | #ifndef NDEBUG |
| 104 | double AvgNetEvDelay = 0; |
| 105 | #endif |
| 106 | |
| 107 | bool C4InteractiveThread::PopEvent(C4InteractiveEventType *pEventType, std::any *data) // (by main thread) |
| 108 | { |
| 109 | CStdLock PopLock(&EventPopCSec); |
| 110 | if (!pFirstEvent) return false; |
| 111 | // get event |
| 112 | Event *pEvent = pFirstEvent->Next; |
| 113 | if (!pEvent) return false; |
| 114 | // return |
| 115 | if (pEventType) |
| 116 | *pEventType = pEvent->Type; |
| 117 | if (data) |
| 118 | *data = std::move(pEvent->Data); |
| 119 | #ifndef NDEBUG |
| 120 | if (Game.IsRunning) |
| 121 | AvgNetEvDelay += ((timeGetTime() - pEvent->Time) - AvgNetEvDelay) / 100; |
| 122 | #endif |
| 123 | // remove |
| 124 | delete pFirstEvent; |
| 125 | pFirstEvent = pEvent; |
| 126 | pFirstEvent->Type = Ev_None; |
| 127 | return true; |
| 128 | } |
| 129 | |
| 130 | void C4InteractiveThread::ProcessEvents() // by main thread |
| 131 | { |
| 132 | C4InteractiveEventType eEventType; std::any eventData; |
| 133 | while (PopEvent(pEventType: &eEventType, data: &eventData)) |
| 134 | switch (eEventType) |
| 135 | { |
| 136 | // Execute in main thread |
| 137 | case Ev_ExecuteInMainThread: |
| 138 | std::any_cast<const std::function<void()> &>(any&: eventData)(); |
| 139 | break; |
| 140 | |
| 141 | // Other events: check for a registered handler |
| 142 | default: |
| 143 | if (eEventType >= Ev_None && eEventType <= Ev_Last) |
| 144 | if (pCallbacks[eEventType]) |
| 145 | pCallbacks[eEventType]->OnThreadEvent(eEvent: eEventType, eventData); |
| 146 | // Note that memory might leak if the event wasn't processed.... |
| 147 | } |
| 148 | } |
| 149 | |