]>
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 | ||
30 | template <typename Message> static __attribute__((noinline, cold, noreturn)) | |
31 | void | |
32 | throw_errno (Message&& msg) { | |
33 | throw std::system_error | |
34 | (errno, std::system_category(), std::forward<Message>(msg)); | |
35 | } | |
36 | ||
37 | static inline | |
38 | std::pair<int, int> | |
39 | splitPointer (void* const ptr) noexcept { | |
40 | static_assert (sizeof(int) == 4, "splitPointer() requires an 4 byte 'int'"); | |
d88353b8 PD |
41 | // In theory, we need this assertion. In practice, it prevents compilation |
42 | // on EL6 i386. Without the assertion, everything works. | |
43 | // If you ever run into trouble with this code, please heed the warnings at | |
44 | // http://man7.org/linux/man-pages/man3/makecontext.3.html#NOTES | |
45 | // static_assert (sizeof(uintptr_t) == 8, | |
46 | // "splitPointer() requires an 8 byte 'uintptr_t'"); | |
5cf909f3 AN |
47 | std::pair<int, int> words; |
48 | auto rep = reinterpret_cast<uintptr_t>(ptr); | |
49 | uint32_t const hw = rep >> 32; | |
50 | auto const lw = static_cast<uint32_t>(rep); | |
51 | std::memcpy (&words.first, &hw, 4); | |
52 | std::memcpy (&words.second, &lw, 4); | |
53 | return words; | |
54 | } | |
55 | ||
56 | template <typename T> static inline | |
57 | T* | |
58 | joinPtr (int const first, int const second) noexcept { | |
59 | static_assert (sizeof(int) == 4, "joinPtr() requires an 4 byte 'int'"); | |
d88353b8 PD |
60 | // See above. |
61 | // static_assert (sizeof(uintptr_t) == 8, | |
62 | // "joinPtr() requires an 8 byte 'uintptr_t'"); | |
5cf909f3 AN |
63 | uint32_t hw; |
64 | uint32_t lw; | |
65 | std::memcpy (&hw, &first, 4); | |
66 | std::memcpy (&lw, &second, 4); | |
67 | return reinterpret_cast<T*>((static_cast<uintptr_t>(hw) << 32) | lw); | |
68 | } | |
69 | ||
70 | extern "C" { | |
71 | static | |
72 | void | |
73 | threadWrapper (int const ctx0, int const ctx1, int const fun0, int const fun1) { | |
74 | auto ctx = joinPtr<pdns_ucontext_t>(ctx0, ctx1); | |
75 | try { | |
abc75058 | 76 | auto start = std::move(*joinPtr<boost::function<void()>>(fun0, fun1)); |
5cf909f3 AN |
77 | start(); |
78 | } catch (...) { | |
79 | ctx->exception = std::current_exception(); | |
80 | } | |
81 | } | |
82 | } // extern "C" | |
83 | ||
84 | pdns_ucontext_t::pdns_ucontext_t() { | |
85 | uc_mcontext = new ::ucontext_t(); | |
86 | uc_link = nullptr; | |
87 | } | |
88 | ||
89 | pdns_ucontext_t::~pdns_ucontext_t() { | |
e168c4d3 | 90 | delete static_cast<ucontext_t*>(uc_mcontext); |
5cf909f3 AN |
91 | } |
92 | ||
93 | void | |
94 | pdns_swapcontext | |
95 | (pdns_ucontext_t& __restrict octx, pdns_ucontext_t const& __restrict ctx) { | |
14ef78e5 PD |
96 | if (::swapcontext (static_cast<ucontext_t*>(octx.uc_mcontext), |
97 | static_cast<ucontext_t*>(ctx.uc_mcontext))) { | |
5cf909f3 AN |
98 | throw_errno ("swapcontext() failed"); |
99 | } | |
100 | if (ctx.exception) { | |
101 | std::rethrow_exception (ctx.exception); | |
102 | } | |
103 | } | |
104 | ||
105 | void | |
106 | pdns_makecontext | |
abc75058 | 107 | (pdns_ucontext_t& ctx, boost::function<void(void)>& start) { |
5cf909f3 AN |
108 | assert (ctx.uc_link); |
109 | assert (ctx.uc_stack.size()); | |
110 | ||
14ef78e5 PD |
111 | auto const mcp = static_cast<ucontext_t*>(ctx.uc_mcontext); |
112 | auto const next = static_cast<ucontext_t*>(ctx.uc_link->uc_mcontext); | |
5cf909f3 AN |
113 | if (::getcontext (mcp)) { |
114 | throw_errno ("getcontext() failed"); | |
115 | } | |
116 | mcp->uc_link = next; | |
117 | mcp->uc_stack.ss_sp = ctx.uc_stack.data(); | |
118 | mcp->uc_stack.ss_size = ctx.uc_stack.size(); | |
119 | mcp->uc_stack.ss_flags = 0; | |
120 | ||
121 | auto ctxarg = splitPointer (&ctx); | |
122 | auto funarg = splitPointer (&start); | |
123 | return ::makecontext (mcp, reinterpret_cast<void(*)(void)>(&threadWrapper), | |
124 | 4, ctxarg.first, ctxarg.second, | |
125 | funarg.first, funarg.second); | |
126 | } |