| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #pragma once |
| |
| class MessageBase |
| { |
| private: |
| unsigned int m_time; |
| unsigned int m_id; |
| |
| static unsigned int s_messageCount; |
| |
| MessageBase(const MessageBase&); |
| |
| public: |
| MessageBase(unsigned int time) : m_time(time), m_id(s_messageCount++) { } |
| virtual ~MessageBase() { } |
| |
| void BeginTimer() { m_time += GetTickCount(); }; |
| unsigned int GetTime() { return m_time; }; |
| unsigned int GetId() { return m_id; }; |
| |
| virtual HRESULT Call(LPCSTR fileName) = 0; |
| }; |
| |
| template <typename T> |
| class SortedList |
| { |
| template <typename T> |
| struct DListNode |
| { |
| T data; |
| DListNode<T>* prev; |
| DListNode<T>* next; |
| |
| public: |
| DListNode(const T& data) : |
| data(data), |
| prev(nullptr), |
| next(nullptr) |
| { } |
| }; |
| |
| public: |
| SortedList(): |
| head(nullptr) |
| { |
| } |
| |
| ~SortedList() |
| { |
| while (head != nullptr) |
| { |
| Remove(head); |
| } |
| } |
| |
| // Scan through the sorted list |
| // Insert before the first node that satisfies the LessThan function |
| // This function maintains the invariant that the list is always sorted |
| void Insert(const T& data) |
| { |
| DListNode<T>* curr = head; |
| DListNode<T>* node = new DListNode<T>(data); |
| DListNode<T>* prev = nullptr; |
| |
| // Now, if we have to insert, we have to insert *after* some node |
| while (curr != nullptr) |
| { |
| if (T::LessThan(data, curr->data)) |
| { |
| break; |
| } |
| prev = curr; |
| curr = curr->next; |
| } |
| |
| InsertAfter(node, prev); |
| } |
| |
| T Pop() |
| { |
| T data = head->data; |
| Remove(head); |
| return data; |
| } |
| |
| template <typename PredicateFn> |
| void Remove(PredicateFn fn) |
| { |
| DListNode<T>* node = head; |
| |
| while (node != nullptr) |
| { |
| if (fn(node->data)) |
| { |
| Remove(node); |
| return; |
| } |
| } |
| } |
| |
| template <typename PredicateFn> |
| void RemoveAll(PredicateFn fn) |
| { |
| while (head != nullptr) |
| { |
| fn(head->data); |
| Remove(head); |
| } |
| } |
| |
| bool IsEmpty() |
| { |
| return head == nullptr; |
| } |
| |
| private: |
| void Remove(DListNode<T>* node) |
| { |
| if (node->prev == nullptr) |
| { |
| head = node->next; |
| } |
| else |
| { |
| node->prev->next = node->next; |
| } |
| |
| if (node->next != nullptr) |
| { |
| node->next->prev = node->prev; |
| } |
| |
| delete node; |
| } |
| |
| void InsertAfter(DListNode<T>* newNode, DListNode<T>* node) |
| { |
| // If the list is empty, just set head to newNode |
| if (head == nullptr) |
| { |
| Assert(node == nullptr); |
| head = newNode; |
| return; |
| } |
| |
| // If node is null here, we must be trying to insert before head |
| if (node == nullptr) |
| { |
| newNode->next = head; |
| head->prev = newNode; |
| head = newNode; |
| return; |
| } |
| |
| Assert(node); |
| newNode->next = node->next; |
| newNode->prev = node; |
| |
| if (node->next) |
| { |
| node->next->prev = newNode; |
| } |
| |
| node->next = newNode; |
| } |
| |
| DListNode<T>* head; |
| }; |
| |
| class MessageQueue |
| { |
| struct ListEntry |
| { |
| unsigned int time; |
| MessageBase* message; |
| |
| ListEntry(unsigned int time, MessageBase* message): |
| time(time), |
| message(message) |
| { } |
| |
| static bool LessThan(const ListEntry& first, const ListEntry& second) |
| { |
| return first.time < second.time; |
| } |
| }; |
| |
| SortedList<ListEntry> m_queue; |
| |
| public: |
| void InsertSorted(MessageBase *message) |
| { |
| message->BeginTimer(); |
| unsigned int time = message->GetTime(); |
| m_queue.Insert(ListEntry(time, message)); |
| } |
| |
| MessageBase* PopAndWait() |
| { |
| Assert(!m_queue.IsEmpty()); |
| |
| ListEntry entry = m_queue.Pop(); |
| MessageBase *tmp = entry.message; |
| |
| int waitTime = tmp->GetTime() - GetTickCount(); |
| if(waitTime > 0) |
| { |
| Sleep(waitTime); |
| } |
| |
| return tmp; |
| } |
| |
| bool IsEmpty() |
| { |
| return m_queue.IsEmpty(); |
| } |
| |
| void RemoveById(unsigned int id) |
| { |
| // Search for the message with the correct id, and delete it. Can be updated |
| // to a hash to improve speed, if necessary. |
| m_queue.Remove([id](const ListEntry& entry) |
| { |
| MessageBase *msg = entry.message; |
| if(msg->GetId() == id) |
| { |
| delete msg; |
| return true; |
| } |
| |
| return false; |
| }); |
| } |
| |
| void RemoveAll() |
| { |
| m_queue.RemoveAll([](const ListEntry& entry) { |
| MessageBase* msg = entry.message; |
| delete msg; |
| }); |
| } |
| |
| HRESULT ProcessAll(LPCSTR fileName) |
| { |
| while(!IsEmpty()) |
| { |
| MessageBase *msg = PopAndWait(); |
| |
| // Omit checking return value for async function, since it shouldn't affect others. |
| msg->Call(fileName); |
| delete msg; |
| |
| ChakraRTInterface::JsTTDNotifyYield(); |
| } |
| return S_OK; |
| } |
| }; |
| |
| // |
| // A custom message helper class to assist defining messages handled by callback functions. |
| // |
| template <class Func, class CustomBase> |
| class CustomMessage : public CustomBase |
| { |
| private: |
| Func m_func; |
| |
| public: |
| CustomMessage(unsigned int time, JsValueRef customArg, const Func& func) : |
| CustomBase(time, customArg), m_func(func) |
| {} |
| |
| virtual HRESULT Call(LPCSTR fileName) override |
| { |
| return m_func(*this); |
| } |
| }; |