From: Otto Moerbeek Date: Tue, 12 Dec 2023 09:13:08 +0000 (+0100) Subject: Move (instead of include) mtasker.cc to mtasker.hh X-Git-Tag: auth-4.9.0-alpha1~14^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6360909552941f76c7d0cb74e791d5a8c53c03cb;p=thirdparty%2Fpdns.git Move (instead of include) mtasker.cc to mtasker.hh --- diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 36285b9546..6b4badc246 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -66,7 +66,6 @@ EXTRA_DIST = \ lua_hpp.mk \ malloctrace.cc malloctrace.hh \ mkpubsuffixcc \ - mtasker.cc \ mtasker_fcontext.cc mtasker_ucontext.cc \ NOTICE \ opensslsigners.hh opensslsigners.cc \ diff --git a/pdns/recursordist/mtasker.cc b/pdns/recursordist/mtasker.cc deleted file mode 100644 index dddd2e77f6..0000000000 --- a/pdns/recursordist/mtasker.cc +++ /dev/null @@ -1,473 +0,0 @@ -/* - * This file is part of PowerDNS or dnsdist. - * Copyright -- PowerDNS.COM B.V. and its contributors - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * In addition, for the avoidance of any doubt, permission is granted to - * link this program with OpenSSL and to (re)distribute the binaries - * produced as the result of such linking. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include "mtasker.hh" -#include "misc.hh" -#include -#include - -#ifdef PDNS_USE_VALGRIND -#include -#endif /* PDNS_USE_VALGRIND */ - -/** \page MTasker - Simple system for implementing cooperative multitasking of functions, with - support for waiting on events which can return values. - - \section copyright Copyright and License - MTasker is (c) 2002 - 2009 by bert hubert. It is licensed to you under the terms of the GPL version 2. - - \section overview High level overview - MTasker is designed to support very simple cooperative multitasking to facilitate writing - code that would ordinarily require a statemachine, for which the author does not consider - himself smart enough. - - This class does not perform any magic it only makes calls to makecontext() and swapcontext(). - Getting the details right however is complicated and MTasker does that for you. - - If preemptive multitasking or more advanced concepts such as semaphores, locks or mutexes - are required, the use of POSIX threads is advised. - - MTasker is designed to offer the performance of statemachines while maintaining simple thread semantics. It is not - a replacement for a full threading system. - - \section compatibility Compatibility - MTasker is only guaranteed to work on Linux with glibc 2.2.5 and higher. It does not work on FreeBSD and notably, - not on Red Hat 6.0. It may work on Solaris, please test. - - \section concepts Concepts - - There are two important concepts, the 'kernel' and the 'thread'. Each thread starts out as a function, - which is passed to MTasker::makeThread(), together with a possible argument. - - This function is now free to do whatever it wants, but realise that MTasker implements cooperative - multitasking, which means that the coder has the responsibility of not taking the CPU overly long. - Other threads can only get the CPU if MTasker::yield() is called or if a thread sleeps to wait for an event, - using the MTasker::waitEvent() method. - - \section kernel The Kernel - The Kernel consists of functions that do housekeeping, but also of code that the client coder - can call to report events. A minimal kernel loop looks like this: - \code - for(;;) { - MT.schedule(); - if(MT.noProcesses()) // exit if no processes are left - break; - } - \endcode - - The kernel typically starts from the main() function of your program. New threads are also - created from the kernel. This can also happen before entering the main loop. To start a thread, - the method MTasker::makeThread is provided. - - \section events Events - By default, Events are recognized by an int and their value is also an int. - This can be overridden by specifying the EventKey and EventVal template parameters. - - An event can be a keypress, but also a UDP packet, or a bit of data from a TCP socket. The - sample code provided works with keypresses, but that is just a not very useful example. - - A thread can also wait for an event only for a limited time, and receive a timeout of that - event did not occur within the specified timeframe. - - \section example A simple menu system - \code -MTasker<> MT; - -void menuHandler(void *p) -{ - int num=(int)p; - cout<<"Key handler for key "< -int MTasker::waitEvent(EventKey& key, EventVal* val, unsigned int timeoutMsec, const struct timeval* now) -{ - if (d_waiters.count(key)) { // there was already an exact same waiter - return -1; - } - - Waiter waiter; - waiter.context = std::make_shared(); - waiter.ttd.tv_sec = 0; - waiter.ttd.tv_usec = 0; - if (timeoutMsec != 0) { - struct timeval increment{}; - increment.tv_sec = timeoutMsec / 1000; - increment.tv_usec = static_cast(1000 * (timeoutMsec % 1000)); - if (now != nullptr) { - waiter.ttd = increment + *now; - } - else { - struct timeval realnow{}; - gettimeofday(&realnow, nullptr); - waiter.ttd = increment + realnow; - } - } - - waiter.tid = d_tid; - waiter.key = key; - - d_waiters.insert(waiter); -#ifdef MTASKERTIMING - unsigned int diff = d_threads[d_tid].dt.ndiff() / 1000; - d_threads[d_tid].totTime += diff; -#endif - notifyStackSwitchToKernel(); - pdns_swapcontext(*d_waiters.find(key)->context, d_kernel); // 'A' will return here when 'key' has arrived, hands over control to kernel first - notifyStackSwitchDone(); -#ifdef MTASKERTIMING - d_threads[d_tid].dt.start(); -#endif - if (val && d_waitstatus == Answer) { - *val = d_waitval; - } - d_tid = waiter.tid; - if ((char*)&waiter < d_threads[d_tid].highestStackSeen) { - d_threads[d_tid].highestStackSeen = (char*)&waiter; - } - key = d_eventkey; - return d_waitstatus; -} - -//! yields control to the kernel or other threads -/** Hands over control to the kernel, allowing other processes to run, or events to arrive */ - -template -void MTasker::yield() -{ - d_runQueue.push(d_tid); - notifyStackSwitchToKernel(); - pdns_swapcontext(*d_threads[d_tid].context, d_kernel); // give control to the kernel - notifyStackSwitchDone(); -} - -//! reports that an event took place for which threads may be waiting -/** From the kernel loop, sendEvent can be called to report that something occurred for which there may be waiters. - \param key Key of the event for which threads may be waiting - \param val If non-zero, pointer to the content of the event - \return Returns -1 in case of error, 0 if there were no waiters, 1 if a thread was woken up. - - WARNING: when passing val as zero, d_waitval is undefined, and hence waitEvent will return undefined! -*/ -template -int MTasker::sendEvent(const EventKey& key, const EventVal* val) -{ - typename waiters_t::iterator waiter = d_waiters.find(key); - - if (waiter == d_waiters.end()) { - // cerr<<"Event sent nobody was waiting for! " <tid; // set tid - d_eventkey = waiter->key; // pass waitEvent the exact key it was woken for - auto userspace = std::move(waiter->context); - d_waiters.erase(waiter); // removes the waitpoint - notifyStackSwitch(d_threads[d_tid].startOfStack, d_stacksize); - try { - pdns_swapcontext(d_kernel, *userspace); // swaps back to the above point 'A' - } - catch (...) { - notifyStackSwitchDone(); - throw; - } - notifyStackSwitchDone(); - return 1; -} - -template -std::shared_ptr MTasker::getUContext() -{ - auto ucontext = std::make_shared(); - if (d_cachedStacks.empty()) { - ucontext->uc_stack.resize(d_stacksize + 1); - } - else { - ucontext->uc_stack = std::move(d_cachedStacks.top()); - d_cachedStacks.pop(); - } - - ucontext->uc_link = &d_kernel; // come back to kernel after dying - -#ifdef PDNS_USE_VALGRIND - uc->valgrind_id = VALGRIND_STACK_REGISTER(&uc->uc_stack[0], - &uc->uc_stack[uc->uc_stack.size() - 1]); -#endif /* PDNS_USE_VALGRIND */ - - return ucontext; -} - -//! launches a new thread -/** The kernel can call this to make a new thread, which starts at the function start and gets passed the val void pointer. - \param start Pointer to the function which will form the start of the thread - \param val A void pointer that can be used to pass data to the thread -*/ -template -void MTasker::makeThread(tfunc_t* start, void* val) -{ - auto ucontext = getUContext(); - - ++d_threadsCount; - auto& thread = d_threads[d_maxtid]; - // we will get a better approximation when the task is executed, but that prevents notifying a stack at nullptr - // on the first invocation - d_threads[d_maxtid].startOfStack = &ucontext->uc_stack[ucontext->uc_stack.size() - 1]; - thread.start = [start, val, this]() { - char dummy{}; - d_threads[d_tid].startOfStack = d_threads[d_tid].highestStackSeen = &dummy; - auto const tid = d_tid; - start(val); - d_zombiesQueue.push(tid); - }; - pdns_makecontext(*ucontext, thread.start); - - thread.context = std::move(ucontext); - d_runQueue.push(d_maxtid++); // will run at next schedule invocation -} - -//! needs to be called periodically so threads can run and housekeeping can be performed -/** The kernel should call this function every once in a while. It makes sense - to call this function if you: - - reported an event - - called makeThread - - want to have threads running waitEvent() to get a timeout if enough time passed - - \return Returns if there is more work scheduled and recalling schedule now would be useful - -*/ -template -bool MTasker::schedule(const struct timeval* now) -{ - if (!d_runQueue.empty()) { - d_tid = d_runQueue.front(); -#ifdef MTASKERTIMING - d_threads[d_tid].dt.start(); -#endif - notifyStackSwitch(d_threads[d_tid].startOfStack, d_stacksize); - try { - pdns_swapcontext(d_kernel, *d_threads[d_tid].context); - } - catch (...) { - notifyStackSwitchDone(); - // It is not clear if the d_runQueue.pop() should be done in this case - throw; - } - notifyStackSwitchDone(); - - d_runQueue.pop(); - return true; - } - if (!d_zombiesQueue.empty()) { - auto zombi = d_zombiesQueue.front(); - if (d_cachedStacks.size() < d_maxCachedStacks) { - auto thread = d_threads.find(zombi); - if (thread != d_threads.end()) { - d_cachedStacks.push(std::move(thread->second.context->uc_stack)); - } - d_threads.erase(thread); - } - else { - d_threads.erase(zombi); - } - --d_threadsCount; - d_zombiesQueue.pop(); - return true; - } - if (!d_waiters.empty()) { - struct timeval rnow{}; - if (now != nullptr) { - gettimeofday(&rnow, nullptr); - } - else { - rnow = *now; - } - typedef typename waiters_t::template index::type waiters_by_ttd_index_t; - // waiters_by_ttd_index_t& ttdindex=d_waiters.template get(); - waiters_by_ttd_index_t& ttdindex = boost::multi_index::get(d_waiters); - - for (typename waiters_by_ttd_index_t::iterator i = ttdindex.begin(); i != ttdindex.end();) { - if (i->ttd.tv_sec && i->ttd < rnow) { - d_waitstatus = TimeOut; - d_eventkey = i->key; // pass waitEvent the exact key it was woken for - auto ucontext = i->context; - d_tid = i->tid; - ttdindex.erase(i++); // removes the waitpoint - - notifyStackSwitch(d_threads[d_tid].startOfStack, d_stacksize); - try { - pdns_swapcontext(d_kernel, *ucontext); // swaps back to the above point 'A' - } - catch (...) { - notifyStackSwitchDone(); - throw; - } - notifyStackSwitchDone(); - } - else if (i->ttd.tv_sec != 0) { - break; - } - else { - ++i; - } - } - } - return false; -} - -//! returns true if there are no processes -/** Call this to check if no processes are running anymore - \return true if no processes are left - */ -template -bool MTasker::noProcesses() const -{ - return d_threadsCount == 0; -} - -//! returns the number of processes running -/** Call this to perhaps limit activities if too many threads are running - \return number of processes running - */ -template -unsigned int MTasker::numProcesses() const -{ - return d_threadsCount; -} - -//! gives access to the list of Events threads are waiting for -/** The kernel can call this to get a list of Events threads are waiting for. This is very useful - to setup 'select' or 'poll' or 'aio' events needed to satisfy these requests. - getEvents clears the events parameter before filling it. - - \param events Vector which is to be filled with keys threads are waiting for -*/ -template -void MTasker::getEvents(std::vector& events) -{ - events.clear(); - for (typename waiters_t::const_iterator i = d_waiters.begin(); i != d_waiters.end(); ++i) { - events.push_back(i->first); - } -} - -//! Returns the current Thread ID (tid) -/** Processes can call this to get a numerical representation of their current thread ID. - This can be useful for logging purposes. -*/ -template -int MTasker::getTid() const -{ - return d_tid; -} - -//! Returns the maximum stack usage so far of this MThread -template -uint64_t MTasker::getMaxStackUsage() -{ - return d_threads[d_tid].startOfStack - d_threads[d_tid].highestStackSeen; -} - -//! Returns the maximum stack usage so far of this MThread -template -unsigned int MTasker::getUsec() -{ -#ifdef MTASKERTIMING - return d_threads[d_tid].totTime + d_threads[d_tid].dt.ndiff() / 1000; -#else - return 0; -#endif -} diff --git a/pdns/recursordist/mtasker.hh b/pdns/recursordist/mtasker.hh index e3103ce083..40ba32d990 100644 --- a/pdns/recursordist/mtasker.hh +++ b/pdns/recursordist/mtasker.hh @@ -156,4 +156,449 @@ private: EventKey d_eventkey; // for waitEvent, contains exact key it was awoken for }; -#include "mtasker.cc" + +#ifdef PDNS_USE_VALGRIND +#include +#endif /* PDNS_USE_VALGRIND */ + +/** \page MTasker + Simple system for implementing cooperative multitasking of functions, with + support for waiting on events which can return values. + + \section copyright Copyright and License + MTasker is (c) 2002 - 2009 by bert hubert. It is licensed to you under the terms of the GPL version 2. + + \section overview High level overview + MTasker is designed to support very simple cooperative multitasking to facilitate writing + code that would ordinarily require a statemachine, for which the author does not consider + himself smart enough. + + This class does not perform any magic it only makes calls to makecontext() and swapcontext(). + Getting the details right however is complicated and MTasker does that for you. + + If preemptive multitasking or more advanced concepts such as semaphores, locks or mutexes + are required, the use of POSIX threads is advised. + + MTasker is designed to offer the performance of statemachines while maintaining simple thread semantics. It is not + a replacement for a full threading system. + + \section compatibility Compatibility + MTasker is only guaranteed to work on Linux with glibc 2.2.5 and higher. It does not work on FreeBSD and notably, + not on Red Hat 6.0. It may work on Solaris, please test. + + \section concepts Concepts + + There are two important concepts, the 'kernel' and the 'thread'. Each thread starts out as a function, + which is passed to MTasker::makeThread(), together with a possible argument. + + This function is now free to do whatever it wants, but realise that MTasker implements cooperative + multitasking, which means that the coder has the responsibility of not taking the CPU overly long. + Other threads can only get the CPU if MTasker::yield() is called or if a thread sleeps to wait for an event, + using the MTasker::waitEvent() method. + + \section kernel The Kernel + The Kernel consists of functions that do housekeeping, but also of code that the client coder + can call to report events. A minimal kernel loop looks like this: + \code + for(;;) { + MT.schedule(); + if(MT.noProcesses()) // exit if no processes are left + break; + } + \endcode + + The kernel typically starts from the main() function of your program. New threads are also + created from the kernel. This can also happen before entering the main loop. To start a thread, + the method MTasker::makeThread is provided. + + \section events Events + By default, Events are recognized by an int and their value is also an int. + This can be overridden by specifying the EventKey and EventVal template parameters. + + An event can be a keypress, but also a UDP packet, or a bit of data from a TCP socket. The + sample code provided works with keypresses, but that is just a not very useful example. + + A thread can also wait for an event only for a limited time, and receive a timeout of that + event did not occur within the specified timeframe. + + \section example A simple menu system + \code +MTasker<> MT; + +void menuHandler(void *p) +{ + int num=(int)p; + cout<<"Key handler for key "< +int MTasker::waitEvent(EventKey& key, EventVal* val, unsigned int timeoutMsec, const struct timeval* now) +{ + if (d_waiters.count(key)) { // there was already an exact same waiter + return -1; + } + + Waiter waiter; + waiter.context = std::make_shared(); + waiter.ttd.tv_sec = 0; + waiter.ttd.tv_usec = 0; + if (timeoutMsec != 0) { + struct timeval increment{}; + increment.tv_sec = timeoutMsec / 1000; + increment.tv_usec = static_cast(1000 * (timeoutMsec % 1000)); + if (now != nullptr) { + waiter.ttd = increment + *now; + } + else { + struct timeval realnow{}; + gettimeofday(&realnow, nullptr); + waiter.ttd = increment + realnow; + } + } + + waiter.tid = d_tid; + waiter.key = key; + + d_waiters.insert(waiter); +#ifdef MTASKERTIMING + unsigned int diff = d_threads[d_tid].dt.ndiff() / 1000; + d_threads[d_tid].totTime += diff; +#endif + notifyStackSwitchToKernel(); + pdns_swapcontext(*d_waiters.find(key)->context, d_kernel); // 'A' will return here when 'key' has arrived, hands over control to kernel first + notifyStackSwitchDone(); +#ifdef MTASKERTIMING + d_threads[d_tid].dt.start(); +#endif + if (val && d_waitstatus == Answer) { + *val = d_waitval; + } + d_tid = waiter.tid; + if ((char*)&waiter < d_threads[d_tid].highestStackSeen) { + d_threads[d_tid].highestStackSeen = (char*)&waiter; + } + key = d_eventkey; + return d_waitstatus; +} + +//! yields control to the kernel or other threads +/** Hands over control to the kernel, allowing other processes to run, or events to arrive */ + +template +void MTasker::yield() +{ + d_runQueue.push(d_tid); + notifyStackSwitchToKernel(); + pdns_swapcontext(*d_threads[d_tid].context, d_kernel); // give control to the kernel + notifyStackSwitchDone(); +} + +//! reports that an event took place for which threads may be waiting +/** From the kernel loop, sendEvent can be called to report that something occurred for which there may be waiters. + \param key Key of the event for which threads may be waiting + \param val If non-zero, pointer to the content of the event + \return Returns -1 in case of error, 0 if there were no waiters, 1 if a thread was woken up. + + WARNING: when passing val as zero, d_waitval is undefined, and hence waitEvent will return undefined! +*/ +template +int MTasker::sendEvent(const EventKey& key, const EventVal* val) +{ + typename waiters_t::iterator waiter = d_waiters.find(key); + + if (waiter == d_waiters.end()) { + // cerr<<"Event sent nobody was waiting for! " <tid; // set tid + d_eventkey = waiter->key; // pass waitEvent the exact key it was woken for + auto userspace = std::move(waiter->context); + d_waiters.erase(waiter); // removes the waitpoint + notifyStackSwitch(d_threads[d_tid].startOfStack, d_stacksize); + try { + pdns_swapcontext(d_kernel, *userspace); // swaps back to the above point 'A' + } + catch (...) { + notifyStackSwitchDone(); + throw; + } + notifyStackSwitchDone(); + return 1; +} + +template +std::shared_ptr MTasker::getUContext() +{ + auto ucontext = std::make_shared(); + if (d_cachedStacks.empty()) { + ucontext->uc_stack.resize(d_stacksize + 1); + } + else { + ucontext->uc_stack = std::move(d_cachedStacks.top()); + d_cachedStacks.pop(); + } + + ucontext->uc_link = &d_kernel; // come back to kernel after dying + +#ifdef PDNS_USE_VALGRIND + uc->valgrind_id = VALGRIND_STACK_REGISTER(&uc->uc_stack[0], + &uc->uc_stack[uc->uc_stack.size() - 1]); +#endif /* PDNS_USE_VALGRIND */ + + return ucontext; +} + +//! launches a new thread +/** The kernel can call this to make a new thread, which starts at the function start and gets passed the val void pointer. + \param start Pointer to the function which will form the start of the thread + \param val A void pointer that can be used to pass data to the thread +*/ +template +void MTasker::makeThread(tfunc_t* start, void* val) +{ + auto ucontext = getUContext(); + + ++d_threadsCount; + auto& thread = d_threads[d_maxtid]; + // we will get a better approximation when the task is executed, but that prevents notifying a stack at nullptr + // on the first invocation + d_threads[d_maxtid].startOfStack = &ucontext->uc_stack[ucontext->uc_stack.size() - 1]; + thread.start = [start, val, this]() { + char dummy{}; + d_threads[d_tid].startOfStack = d_threads[d_tid].highestStackSeen = &dummy; + auto const tid = d_tid; + start(val); + d_zombiesQueue.push(tid); + }; + pdns_makecontext(*ucontext, thread.start); + + thread.context = std::move(ucontext); + d_runQueue.push(d_maxtid++); // will run at next schedule invocation +} + +//! needs to be called periodically so threads can run and housekeeping can be performed +/** The kernel should call this function every once in a while. It makes sense + to call this function if you: + - reported an event + - called makeThread + - want to have threads running waitEvent() to get a timeout if enough time passed + + \return Returns if there is more work scheduled and recalling schedule now would be useful + +*/ +template +bool MTasker::schedule(const struct timeval* now) +{ + if (!d_runQueue.empty()) { + d_tid = d_runQueue.front(); +#ifdef MTASKERTIMING + d_threads[d_tid].dt.start(); +#endif + notifyStackSwitch(d_threads[d_tid].startOfStack, d_stacksize); + try { + pdns_swapcontext(d_kernel, *d_threads[d_tid].context); + } + catch (...) { + notifyStackSwitchDone(); + // It is not clear if the d_runQueue.pop() should be done in this case + throw; + } + notifyStackSwitchDone(); + + d_runQueue.pop(); + return true; + } + if (!d_zombiesQueue.empty()) { + auto zombi = d_zombiesQueue.front(); + if (d_cachedStacks.size() < d_maxCachedStacks) { + auto thread = d_threads.find(zombi); + if (thread != d_threads.end()) { + d_cachedStacks.push(std::move(thread->second.context->uc_stack)); + } + d_threads.erase(thread); + } + else { + d_threads.erase(zombi); + } + --d_threadsCount; + d_zombiesQueue.pop(); + return true; + } + if (!d_waiters.empty()) { + struct timeval rnow{}; + if (now != nullptr) { + gettimeofday(&rnow, nullptr); + } + else { + rnow = *now; + } + typedef typename waiters_t::template index::type waiters_by_ttd_index_t; + // waiters_by_ttd_index_t& ttdindex=d_waiters.template get(); + waiters_by_ttd_index_t& ttdindex = boost::multi_index::get(d_waiters); + + for (typename waiters_by_ttd_index_t::iterator i = ttdindex.begin(); i != ttdindex.end();) { + if (i->ttd.tv_sec && i->ttd < rnow) { + d_waitstatus = TimeOut; + d_eventkey = i->key; // pass waitEvent the exact key it was woken for + auto ucontext = i->context; + d_tid = i->tid; + ttdindex.erase(i++); // removes the waitpoint + + notifyStackSwitch(d_threads[d_tid].startOfStack, d_stacksize); + try { + pdns_swapcontext(d_kernel, *ucontext); // swaps back to the above point 'A' + } + catch (...) { + notifyStackSwitchDone(); + throw; + } + notifyStackSwitchDone(); + } + else if (i->ttd.tv_sec != 0) { + break; + } + else { + ++i; + } + } + } + return false; +} + +//! returns true if there are no processes +/** Call this to check if no processes are running anymore + \return true if no processes are left + */ +template +bool MTasker::noProcesses() const +{ + return d_threadsCount == 0; +} + +//! returns the number of processes running +/** Call this to perhaps limit activities if too many threads are running + \return number of processes running + */ +template +unsigned int MTasker::numProcesses() const +{ + return d_threadsCount; +} + +//! gives access to the list of Events threads are waiting for +/** The kernel can call this to get a list of Events threads are waiting for. This is very useful + to setup 'select' or 'poll' or 'aio' events needed to satisfy these requests. + getEvents clears the events parameter before filling it. + + \param events Vector which is to be filled with keys threads are waiting for +*/ +template +void MTasker::getEvents(std::vector& events) +{ + events.clear(); + for (typename waiters_t::const_iterator i = d_waiters.begin(); i != d_waiters.end(); ++i) { + events.push_back(i->first); + } +} + +//! Returns the current Thread ID (tid) +/** Processes can call this to get a numerical representation of their current thread ID. + This can be useful for logging purposes. +*/ +template +int MTasker::getTid() const +{ + return d_tid; +} + +//! Returns the maximum stack usage so far of this MThread +template +uint64_t MTasker::getMaxStackUsage() +{ + return d_threads[d_tid].startOfStack - d_threads[d_tid].highestStackSeen; +} + +//! Returns the maximum stack usage so far of this MThread +template +unsigned int MTasker::getUsec() +{ +#ifdef MTASKERTIMING + return d_threads[d_tid].totTime + d_threads[d_tid].dt.ndiff() / 1000; +#else + return 0; +#endif +} +