--- /dev/null
+#ifndef LAZY_ALLOCATOR_HH
+#define LAZY_ALLOCATOR_HH
+
+#include <cstddef>
+#include <utility>
+#include <type_traits>
+
+template <typename T>
+struct lazy_allocator {
+ using value_type = T;
+ using pointer = T*;
+ using size_type = std::size_t;
+ static_assert (std::is_trivial<T>::value,
+ "lazy_allocator must only be used with trivial types");
+
+ pointer
+ allocate (size_type const n) {
+ return static_cast<pointer>(::operator new (n * sizeof(value_type)));
+ }
+
+ void
+ deallocate (pointer const ptr, size_type const n) noexcept {
+#if defined(__cpp_sized_deallocation) && (__cpp_sized_deallocation >= 201309)
+ ::operator delete (ptr, n * sizeof(value_type));
+#else
+ (void) n;
+ ::operator delete (ptr);
+#endif
+ }
+
+ void construct (T*) const noexcept {}
+
+ template <typename X, typename... Args>
+ void
+ construct (X* place, Args&&... args) const noexcept {
+ new (static_cast<void*>(place)) X (std::forward<Args>(args)...);
+ }
+};
+
+template <typename T> inline
+bool operator== (lazy_allocator<T> const&, lazy_allocator<T> const&) noexcept {
+ return true;
+}
+
+template <typename T> inline
+bool operator!= (lazy_allocator<T> const&, lazy_allocator<T> const&) noexcept {
+ return false;
+}
+
+#endif // LAZY_ALLOCATOR_HH
}
Waiter w;
- w.context=new ucontext_t;
+ w.context=std::make_shared<pdns_ucontext_t>();
w.ttd.tv_sec = 0; w.ttd.tv_usec = 0;
if(timeoutMsec) {
struct timeval increment;
unsigned int diff=d_threads[d_tid].dt.ndiff()/1000;
d_threads[d_tid].totTime+=diff;
#endif
- if(swapcontext(d_waiters.find(key)->context,&d_kernel)) { // 'A' will return here when 'key' has arrived, hands over control to kernel first
- perror("swapcontext");
- exit(EXIT_FAILURE); // no way we can deal with this
- }
+ pdns_swapcontext(*d_waiters.find(key)->context,d_kernel); // 'A' will return here when 'key' has arrived, hands over control to kernel first
#ifdef MTASKERTIMING
d_threads[d_tid].dt.start();
#endif
template<class Key, class Val>void MTasker<Key,Val>::yield()
{
d_runQueue.push(d_tid);
- if(swapcontext(d_threads[d_tid].context ,&d_kernel) < 0) { // give control to the kernel
- perror("swapcontext in yield");
- exit(EXIT_FAILURE);
- }
+ pdns_swapcontext(*d_threads[d_tid].context ,d_kernel); // give control to the kernel
}
//! reports that an event took place for which threads may be waiting
if(val)
d_waitval=*val;
- ucontext_t *userspace=waiter->context;
d_tid=waiter->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
- if(swapcontext(&d_kernel,userspace)) { // swaps back to the above point 'A'
- perror("swapcontext in sendEvent");
- exit(EXIT_FAILURE);
- }
- delete userspace;
+ pdns_swapcontext(d_kernel,*userspace); // swaps back to the above point 'A'
return 1;
}
-inline pair<uint32_t, uint32_t> splitPointer(void *ptr)
-{
- uint64_t ll = (uint64_t) ptr;
- return make_pair(ll >> 32, ll & 0xffffffff);
-}
-
-inline void* joinPtr(uint32_t val1, uint32_t val2)
-{
- return (void*)(((uint64_t)val1 << 32) | (uint64_t)val2);
-}
-
//! 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
*/
template<class Key, class Val>void MTasker<Key,Val>::makeThread(tfunc_t *start, void* val)
{
- ucontext_t *uc=new ucontext_t;
- getcontext(uc);
+ auto uc=std::make_shared<pdns_ucontext_t>();
uc->uc_link = &d_kernel; // come back to kernel after dying
- uc->uc_stack.ss_sp = new char[d_stacksize];
-
- uc->uc_stack.ss_size = d_stacksize;
- pair<uint32_t, uint32_t> valpair = splitPointer(val);
- pair<uint32_t, uint32_t> thispair = splitPointer(this);
-
- makecontext (uc, (void (*)(void))threadWrapper, 6, thispair.first, thispair.second, start, d_maxtid, valpair.first, valpair.second);
-
- d_threads[d_maxtid].context = uc;
+ uc->uc_stack.resize (d_stacksize);
+
+ auto& thread = d_threads[d_maxtid];
+ auto mt = this;
+ thread.start = [start, val, mt]() {
+ char dummy;
+ mt->d_threads[mt->d_tid].startOfStack = mt->d_threads[mt->d_tid].highestStackSeen = &dummy;
+ auto const tid = mt->d_tid;
+ start (val);
+ mt->d_zombiesQueue.push(tid);
+ };
+ pdns_makecontext (*uc, thread.start);
+
+ thread.context = std::move(uc);
d_runQueue.push(d_maxtid++); // will run at next schedule invocation
}
#ifdef MTASKERTIMING
d_threads[d_tid].dt.start();
#endif
- if(swapcontext(&d_kernel, d_threads[d_tid].context)) {
- perror("swapcontext in schedule");
- exit(EXIT_FAILURE);
- }
+ pdns_swapcontext(d_kernel, *d_threads[d_tid].context);
d_runQueue.pop();
return true;
}
if(!d_zombiesQueue.empty()) {
- delete[] (char *)d_threads[d_zombiesQueue.front()].context->uc_stack.ss_sp;
- delete d_threads[d_zombiesQueue.front()].context;
d_threads.erase(d_zombiesQueue.front());
d_zombiesQueue.pop();
return true;
if(i->ttd.tv_sec && i->ttd < rnow) {
d_waitstatus=TimeOut;
d_eventkey=i->key; // pass waitEvent the exact key it was woken for
- ucontext_t* uc = i->context;
+ auto uc = i->context;
d_tid = i->tid;
ttdindex.erase(i++); // removes the waitpoint
- if(swapcontext(&d_kernel, uc)) { // swaps back to the above point 'A'
- perror("swapcontext in schedule2");
- exit(EXIT_FAILURE);
- }
- delete uc;
+ pdns_swapcontext(d_kernel, *uc); // swaps back to the above point 'A'
}
else if(i->ttd.tv_sec)
break;
}
}
-template<class Key, class Val>void MTasker<Key,Val>::threadWrapper(uint32_t self1, uint32_t self2, tfunc_t *tf, int tid, uint32_t val1, uint32_t val2)
-{
- void* val = joinPtr(val1, val2);
- MTasker* self = (MTasker*) joinPtr(self1, self2);
- self->d_threads[self->d_tid].startOfStack = self->d_threads[self->d_tid].highestStackSeen = (char*)&val;
- (*tf)(val);
- self->d_zombiesQueue.push(tid);
-
- // we now jump to &kernel, automatically
-}
-
//! 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.
#ifndef MTASKER_HH
#define MTASKER_HH
#include <stdint.h>
-#include <signal.h>
-#include <ucontext.h>
-// don't pollute the namespace with the DS register (i386 only)
-#undef DS
#include <queue>
#include <vector>
#include <map>
#include <boost/multi_index/key_extractors.hpp>
#include "namespaces.hh"
#include "misc.hh"
+#include "mtasker_context.hh"
+#include <memory>
using namespace ::boost::multi_index;
// #define MTASKERTIMING 1
\tparam EventVal Type of the content or value of an event. Defaults to int. Cannot be set to void.
\note The EventKey needs to have an operator< defined because it is used as the key of an associative array
*/
+
template<class EventKey=int, class EventVal=int> class MTasker
{
private:
- ucontext_t d_kernel;
+ pdns_ucontext_t d_kernel;
std::queue<int> d_runQueue;
std::queue<int> d_zombiesQueue;
struct ThreadInfo
{
- ucontext_t* context;
+ std::shared_ptr<pdns_ucontext_t> context;
+ std::function<void(void)> start;
char* startOfStack;
char* highestStackSeen;
#ifdef MTASKERTIMING
struct Waiter
{
EventKey key;
- ucontext_t *context;
+ std::shared_ptr<pdns_ucontext_t> context;
struct timeval ttd;
int tid;
};
unsigned int getUsec();
private:
- static void threadWrapper(uint32_t self1, uint32_t self2, tfunc_t *tf, int tid, uint32_t val1, uint32_t val2);
EventKey d_eventkey; // for waitEvent, contains exact key it was awoken for
};
#include "mtasker.cc"
--- /dev/null
+#ifndef MTASKER_CONTEXT_HH
+#define MTASKER_CONTEXT_HH
+
+#include "lazy_allocator.hh"
+#include <functional>
+#include <vector>
+#include <exception>
+
+struct pdns_ucontext_t {
+ pdns_ucontext_t ();
+ pdns_ucontext_t (pdns_ucontext_t const&) = delete;
+ pdns_ucontext_t& operator= (pdns_ucontext_t const&) = delete;
+ ~pdns_ucontext_t ();
+
+ void* uc_mcontext;
+ pdns_ucontext_t* uc_link;
+ std::vector<char, lazy_allocator<char>> uc_stack;
+ std::exception_ptr exception;
+};
+
+void
+pdns_swapcontext
+(pdns_ucontext_t& __restrict octx, pdns_ucontext_t const& __restrict ctx);
+
+void
+pdns_makecontext
+(pdns_ucontext_t& ctx, std::function<void(void)>& start);
+
+#endif // MTASKER_CONTEXT_HH
--- /dev/null
+#include "mtasker_context.hh"
+#include <system_error>
+#include <exception>
+#include <cstring>
+#include <cassert>
+#include <signal.h>
+#include <ucontext.h>
+
+template <typename Message> static __attribute__((noinline, cold, noreturn))
+void
+throw_errno (Message&& msg) {
+ throw std::system_error
+ (errno, std::system_category(), std::forward<Message>(msg));
+}
+
+static inline
+std::pair<int, int>
+splitPointer (void* const ptr) noexcept {
+ static_assert (sizeof(int) == 4, "splitPointer() requires an 4 byte 'int'");
+ static_assert (sizeof(uintptr_t) == 8,
+ "splitPointer() requires an 8 byte 'uintptr_t'");
+ std::pair<int, int> words;
+ auto rep = reinterpret_cast<uintptr_t>(ptr);
+ uint32_t const hw = rep >> 32;
+ auto const lw = static_cast<uint32_t>(rep);
+ std::memcpy (&words.first, &hw, 4);
+ std::memcpy (&words.second, &lw, 4);
+ return words;
+}
+
+template <typename T> static inline
+T*
+joinPtr (int const first, int const second) noexcept {
+ static_assert (sizeof(int) == 4, "joinPtr() requires an 4 byte 'int'");
+ static_assert (sizeof(uintptr_t) == 8,
+ "joinPtr() requires an 8 byte 'uintptr_t'");
+ uint32_t hw;
+ uint32_t lw;
+ std::memcpy (&hw, &first, 4);
+ std::memcpy (&lw, &second, 4);
+ return reinterpret_cast<T*>((static_cast<uintptr_t>(hw) << 32) | lw);
+}
+
+extern "C" {
+static
+void
+threadWrapper (int const ctx0, int const ctx1, int const fun0, int const fun1) {
+ auto ctx = joinPtr<pdns_ucontext_t>(ctx0, ctx1);
+ try {
+ auto start = std::move(*joinPtr<std::function<void()>>(fun0, fun1));
+ start();
+ } catch (...) {
+ ctx->exception = std::current_exception();
+ }
+}
+} // extern "C"
+
+pdns_ucontext_t::pdns_ucontext_t() {
+ uc_mcontext = new ::ucontext_t();
+ uc_link = nullptr;
+}
+
+pdns_ucontext_t::~pdns_ucontext_t() {
+ delete static_cast<::ucontext_t*>(uc_mcontext);
+}
+
+void
+pdns_swapcontext
+(pdns_ucontext_t& __restrict octx, pdns_ucontext_t const& __restrict ctx) {
+ if (::swapcontext (static_cast<::ucontext*>(octx.uc_mcontext),
+ static_cast<::ucontext*>(ctx.uc_mcontext))) {
+ throw_errno ("swapcontext() failed");
+ }
+ if (ctx.exception) {
+ std::rethrow_exception (ctx.exception);
+ }
+}
+
+void
+pdns_makecontext
+(pdns_ucontext_t& ctx, std::function<void(void)>& start) {
+ assert (ctx.uc_link);
+ assert (ctx.uc_stack.size());
+
+ auto const mcp = static_cast<::ucontext*>(ctx.uc_mcontext);
+ auto const next = static_cast<::ucontext*>(ctx.uc_link->uc_mcontext);
+ if (::getcontext (mcp)) {
+ throw_errno ("getcontext() failed");
+ }
+ mcp->uc_link = next;
+ mcp->uc_stack.ss_sp = ctx.uc_stack.data();
+ mcp->uc_stack.ss_size = ctx.uc_stack.size();
+ mcp->uc_stack.ss_flags = 0;
+
+ auto ctxarg = splitPointer (&ctx);
+ auto funarg = splitPointer (&start);
+ return ::makecontext (mcp, reinterpret_cast<void(*)(void)>(&threadWrapper),
+ 4, ctxarg.first, ctxarg.second,
+ funarg.first, funarg.second);
+}
iputils.hh iputils.cc \
ixfr.cc ixfr.hh \
json.cc json.hh \
+ lazy_allocator.hh \
lock.hh \
logger.hh logger.cc \
lua-recursor4.cc lua-recursor4.hh \
misc.hh misc.cc \
mplexer.hh \
mtasker.hh \
+ mtasker_context.hh mtasker_ucontext.cc \
namespaces.hh \
nsecrecords.cc \
opensslsigners.cc opensslsigners.hh \
--- /dev/null
+../lazy_allocator.hh
\ No newline at end of file
--- /dev/null
+../mtasker_context.hh
\ No newline at end of file
--- /dev/null
+../mtasker_ucontext.cc
\ No newline at end of file