/* Copyright ©2006 Ricardo Pescuma Domenecci Modified ©2008-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdafx.h" static int QueueSortItems(const CQueueItem *i1, const CQueueItem *i2) { int rc = i1->check_time - i2->check_time; if (!rc) rc = i1->hContact != i2->hContact; return rc; } ///////////////////////////////////////////////////////////////////////////////////////// // Constructor & destructor CContactQueue::CContactQueue(int initialSize) : _queue(initialSize, QueueSortItems) { _hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); _status = RUNNING; mir_forkthread((pThreadFunc)CContactQueue::ThreadProc, this); } CContactQueue::~CContactQueue() { if (_status == RUNNING) _status = STOPPING; SetEvent(_hEvent); for (int count = 0; _status != STOPPED && ++count < 50;) Sleep(10); for (int i = 0; i < _queue.getCount(); i++) mir_free(_queue[i]); CloseHandle(_hEvent); } void CContactQueue::Remove(int idx) { CQueueItem *qi = _queue[idx]; _queue.remove(idx); mir_free(qi); } ///////////////////////////////////////////////////////////////////////////////////////// // This function removes all queue items. void CContactQueue::RemoveAll() { mir_cslock lck(_cs); for (int i = _queue.getCount() - 1; i >= 0; --i) mir_free(_queue[i]); _queue.destroy(); } ///////////////////////////////////////////////////////////////////////////////////////// // This function removes all queue items for the hContact. void CContactQueue::RemoveAll(MCONTACT hContact) { mir_cslock lck(_cs); for (int i = _queue.getCount() - 1; i >= 0; --i) { CQueueItem *qi = _queue[i]; if (qi->hContact == hContact) { _queue.remove(i); mir_free(qi); } } } ///////////////////////////////////////////////////////////////////////////////////////// // This function removes all queue items for the hContact considering the correct parameter. void CContactQueue::RemoveAllConsiderParam(MCONTACT hContact, PVOID param) { mir_cslock lck(_cs); for (int i = _queue.getCount() - 1; i >= 0; --i) { CQueueItem *qi = _queue[i]; if (qi->hContact == hContact && qi->param == param) { _queue.remove(i); mir_free(qi); } } } ///////////////////////////////////////////////////////////////////////////////////////// // This method adds the desired new item. BOOL CContactQueue::Add(int waitTime, MCONTACT hContact, PVOID param) { mir_cslock lck(_cs); return InternalAdd(waitTime, hContact, param); } ///////////////////////////////////////////////////////////////////////////////////////// // This method adds the desired new item only, if the queue does not yet contain // an item for the contact. BOOL CContactQueue::AddIfDontHave(int waitTime, MCONTACT hContact, PVOID param) { mir_cslock lck(_cs); for (int i = _queue.getCount() - 1; i >= 0; --i) if (_queue[i]->hContact == hContact) return FALSE; return InternalAdd(waitTime, hContact, param); } ///////////////////////////////////////////////////////////////////////////////////////// // This method removes all existing queue items for the contact and adds a new queue item // for the given contact. This method might be used to move an existing entry, // whose check_time has changed. BOOL CContactQueue::AddUnique(int waitTime, MCONTACT hContact, PVOID param) { mir_cslock lck(_cs); RemoveAll(hContact); return InternalAdd(waitTime, hContact, param); } ///////////////////////////////////////////////////////////////////////////////////////// // This method removes all existing queue items for the contact with the same parameter as @e param // and adds a new queue item for the given contact. This method might be used to move an existing // entry, whose check_time has changed. BOOL CContactQueue::AddUniqueConsiderParam(int waitTime, MCONTACT hContact, PVOID param) { mir_cslock lck(_cs); RemoveAllConsiderParam(hContact, param); return InternalAdd(waitTime, hContact, param); } ///////////////////////////////////////////////////////////////////////////////////////// // This member function really adds an item into the time sorted queue list. BOOL CContactQueue::InternalAdd(int waitTime, MCONTACT hContact, PVOID param) { CQueueItem *qi = (CQueueItem *) mir_alloc(sizeof(CQueueItem)); qi->hContact = hContact; qi->check_time = GetTickCount() + waitTime; qi->param = param; int rc = _queue.insert(qi); if (!rc) mir_free(qi); SetEvent(_hEvent); return rc; } ///////////////////////////////////////////////////////////////////////////////////////// // This is the real thread callback function. As long as _status // is set to RUNNING it looks for items in the queue to perform // the _pfnCallback function on them. If the queue is empty or the // next upcoming item is located in the future, the thread is suspended // in the meanwhile. void CContactQueue::Thread() { while (_status == RUNNING) { ResetEvent(_hEvent); mir_cslockfull lck(_cs); if (_queue.getCount() <= 0) { // can be used by a derivant OnEmpty(); lck.unlock(); Suspend(INFINITE); continue; } // Take a look at first queue item CQueueItem *qi = _queue[0]; int dt = qi->check_time - GetTickCount(); if (dt > 0) { lck.unlock(); Suspend(dt); continue; } // Will request this queue item _queue.remove(0); lck.unlock(); Callback(qi->hContact, qi->param); mir_free(qi); } _status = STOPPED; } ///////////////////////////////////////////////////////////////////////////////////////// // This method suspends the worker thread for the given ammount of time. void CContactQueue::Suspend(int time) const { if (_status == RUNNING) WaitForSingleObject(_hEvent, time); } ///////////////////////////////////////////////////////////////////////////////////////// // This method resumes the worker thread and immitiatly goes on with the next entry. void CContactQueue::ContinueWithNext() { if (_status != RUNNING) return; mir_cslock lck(_cs); int c = _queue.getCount(); if (c > 0) { int dt = _queue[0]->check_time - GetTickCount() - 3000; if (dt > 0) for (int i = 0; i < c; i++) _queue[i]->check_time -= dt; } SetEvent(_hEvent); }