]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
5cf909f3 AN |
22 | #include "mtasker_context.hh" |
23 | #include <system_error> | |
24 | #include <exception> | |
25 | #include <cstring> | |
26 | #include <cassert> | |
27 | #include <signal.h> | |
28 | #include <ucontext.h> | |
29 | ||
ec7fcc43 RG |
30 | #ifdef PDNS_USE_VALGRIND |
31 | #include <valgrind/valgrind.h> | |
32 | #endif /* PDNS_USE_VALGRIND */ | |
33 | ||
9c811931 RG |
34 | #ifdef HAVE_FIBER_SANITIZER |
35 | __thread void* t_mainStack{nullptr}; | |
36 | __thread size_t t_mainStackSize{0}; | |
37 | #endif /* HAVE_FIBER_SANITIZER */ | |
38 | ||
5cf909f3 AN |
39 | template <typename Message> static __attribute__((noinline, cold, noreturn)) |
40 | void | |
41 | throw_errno (Message&& msg) { | |
42 | throw std::system_error | |
43 | (errno, std::system_category(), std::forward<Message>(msg)); | |
44 | } | |
45 | ||
46 | static inline | |
47 | std::pair<int, int> | |
48 | splitPointer (void* const ptr) noexcept { | |
49 | static_assert (sizeof(int) == 4, "splitPointer() requires an 4 byte 'int'"); | |
d88353b8 PD |
50 | // In theory, we need this assertion. In practice, it prevents compilation |
51 | // on EL6 i386. Without the assertion, everything works. | |
52 | // If you ever run into trouble with this code, please heed the warnings at | |
53 | // http://man7.org/linux/man-pages/man3/makecontext.3.html#NOTES | |
54 | // static_assert (sizeof(uintptr_t) == 8, | |
55 | // "splitPointer() requires an 8 byte 'uintptr_t'"); | |
5cf909f3 AN |
56 | std::pair<int, int> words; |
57 | auto rep = reinterpret_cast<uintptr_t>(ptr); | |
58 | uint32_t const hw = rep >> 32; | |
59 | auto const lw = static_cast<uint32_t>(rep); | |
60 | std::memcpy (&words.first, &hw, 4); | |
61 | std::memcpy (&words.second, &lw, 4); | |
62 | return words; | |
63 | } | |
64 | ||
65 | template <typename T> static inline | |
66 | T* | |
67 | joinPtr (int const first, int const second) noexcept { | |
68 | static_assert (sizeof(int) == 4, "joinPtr() requires an 4 byte 'int'"); | |
d88353b8 PD |
69 | // See above. |
70 | // static_assert (sizeof(uintptr_t) == 8, | |
71 | // "joinPtr() requires an 8 byte 'uintptr_t'"); | |
5cf909f3 AN |
72 | uint32_t hw; |
73 | uint32_t lw; | |
74 | std::memcpy (&hw, &first, 4); | |
75 | std::memcpy (&lw, &second, 4); | |
76 | return reinterpret_cast<T*>((static_cast<uintptr_t>(hw) << 32) | lw); | |
77 | } | |
78 | ||
79 | extern "C" { | |
80 | static | |
81 | void | |
82 | threadWrapper (int const ctx0, int const ctx1, int const fun0, int const fun1) { | |
9c811931 | 83 | notifyStackSwitchDone(); |
5cf909f3 AN |
84 | auto ctx = joinPtr<pdns_ucontext_t>(ctx0, ctx1); |
85 | try { | |
abc75058 | 86 | auto start = std::move(*joinPtr<boost::function<void()>>(fun0, fun1)); |
5cf909f3 AN |
87 | start(); |
88 | } catch (...) { | |
89 | ctx->exception = std::current_exception(); | |
90 | } | |
9c811931 | 91 | notifyStackSwitchToKernel(); |
5cf909f3 AN |
92 | } |
93 | } // extern "C" | |
94 | ||
95 | pdns_ucontext_t::pdns_ucontext_t() { | |
96 | uc_mcontext = new ::ucontext_t(); | |
97 | uc_link = nullptr; | |
ec7fcc43 RG |
98 | #ifdef PDNS_USE_VALGRIND |
99 | valgrind_id = 0; | |
100 | #endif /* PDNS_USE_VALGRIND */ | |
5cf909f3 AN |
101 | } |
102 | ||
103 | pdns_ucontext_t::~pdns_ucontext_t() { | |
e168c4d3 | 104 | delete static_cast<ucontext_t*>(uc_mcontext); |
ec7fcc43 RG |
105 | #ifdef PDNS_USE_VALGRIND |
106 | if (valgrind_id != 0) { | |
107 | VALGRIND_STACK_DEREGISTER(valgrind_id); | |
108 | } | |
109 | #endif /* PDNS_USE_VALGRIND */ | |
5cf909f3 AN |
110 | } |
111 | ||
112 | void | |
113 | pdns_swapcontext | |
114 | (pdns_ucontext_t& __restrict octx, pdns_ucontext_t const& __restrict ctx) { | |
14ef78e5 PD |
115 | if (::swapcontext (static_cast<ucontext_t*>(octx.uc_mcontext), |
116 | static_cast<ucontext_t*>(ctx.uc_mcontext))) { | |
5cf909f3 AN |
117 | throw_errno ("swapcontext() failed"); |
118 | } | |
119 | if (ctx.exception) { | |
120 | std::rethrow_exception (ctx.exception); | |
121 | } | |
122 | } | |
123 | ||
124 | void | |
125 | pdns_makecontext | |
abc75058 | 126 | (pdns_ucontext_t& ctx, boost::function<void(void)>& start) { |
5cf909f3 AN |
127 | assert (ctx.uc_link); |
128 | assert (ctx.uc_stack.size()); | |
129 | ||
14ef78e5 PD |
130 | auto const mcp = static_cast<ucontext_t*>(ctx.uc_mcontext); |
131 | auto const next = static_cast<ucontext_t*>(ctx.uc_link->uc_mcontext); | |
5cf909f3 AN |
132 | if (::getcontext (mcp)) { |
133 | throw_errno ("getcontext() failed"); | |
134 | } | |
135 | mcp->uc_link = next; | |
136 | mcp->uc_stack.ss_sp = ctx.uc_stack.data(); | |
43c3c21e | 137 | mcp->uc_stack.ss_size = ctx.uc_stack.size()-1; |
5cf909f3 AN |
138 | mcp->uc_stack.ss_flags = 0; |
139 | ||
140 | auto ctxarg = splitPointer (&ctx); | |
141 | auto funarg = splitPointer (&start); | |
142 | return ::makecontext (mcp, reinterpret_cast<void(*)(void)>(&threadWrapper), | |
143 | 4, ctxarg.first, ctxarg.second, | |
144 | funarg.first, funarg.second); | |
145 | } |