Reland r216808, underlying lldb bug has been fixed.
[WebKit-https.git] / Source / WTF / wtf / ThreadMessage.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ThreadMessage.h"
28
29 #if USE(PTHREADS)
30
31 #include <fcntl.h>
32 #include <unistd.h>
33
34 #include <wtf/DataLog.h>
35 #include <wtf/Lock.h>
36 #include <wtf/Locker.h>
37 #include <wtf/threads/Signals.h>
38
39 #if HAVE(MACH_EXCEPTIONS)
40 #include <mach/thread_act.h>
41 #endif
42
43 namespace WTF {
44
45 using Node = LocklessBag<ThreadMessageData*>::Node;
46
47 class ThreadMessageData {
48     WTF_MAKE_FAST_ALLOCATED;
49 public:
50     ThreadMessageData(const ThreadMessage& m)
51         : ran(nullptr)
52         , message(m)
53     {
54     }
55
56     Atomic<Node*> ran;
57     const ThreadMessage& message;
58 };
59
60 enum FileDescriptor {
61     Read,
62     Write,
63     NumberOfFileDescriptors,
64 };
65
66 static int fileDescriptors[NumberOfFileDescriptors];
67 static const char* const magicByte = "d";
68
69
70 void initializeThreadMessages()
71 {
72     int result = pipe(fileDescriptors);
73     RELEASE_ASSERT(!result);
74
75     int flags = fcntl(fileDescriptors[Write], F_GETFL);
76     result = fcntl(fileDescriptors[Write], F_SETFL, flags | O_NONBLOCK | O_APPEND);
77     flags = fcntl(fileDescriptors[Write], F_GETFL);
78     RELEASE_ASSERT(result != -1);
79     RELEASE_ASSERT((flags & O_NONBLOCK) && (flags & O_APPEND));
80
81     flags = fcntl(fileDescriptors[Read], F_GETFL);
82     result = fcntl(fileDescriptors[Read], F_SETFL, flags & ~O_NONBLOCK);
83     flags = fcntl(fileDescriptors[Read], F_GETFL);
84     RELEASE_ASSERT(result != -1);
85     RELEASE_ASSERT(!(flags & O_NONBLOCK));
86 }
87
88 SUPPRESS_ASAN
89 static MessageStatus sendMessageUsingSignal(Thread& thread, const ThreadMessage& message)
90 {
91     constexpr Signal signal = Signal::Usr;
92     static std::once_flag once;
93     std::call_once(once, [] {
94         installSignalHandler(signal, [] (Signal, SigInfo&, PlatformRegisters& registers) {
95             Thread* thread = Thread::currentMayBeNull();
96
97             if (!thread) {
98                 dataLogLn("We somehow got a message on a thread that didn't have a WTF::Thread initialized, we probably deadlocked, halp.");
99                 return SignalAction::NotHandled;
100             }
101
102             // Node should be deleted in the sender thread. Deleting Nodes in signal handler causes dead lock.
103             thread->threadMessages().consumeAllWithNode([&] (ThreadMessageData* data, Node* node) {
104                 data->message(registers);
105                 // By setting ran variable, (1) the sender acknowledges the completion and
106                 // (2) gets the Node to be deleted.
107                 data->ran.store(node);
108             });
109
110             while (write(fileDescriptors[Write], magicByte, 1) == -1)
111                 ASSERT(errno == EAGAIN);
112
113             return SignalAction::Handled;
114         });
115     });
116
117
118     // Since we are guarenteed not to return until we get a response from the other thread this is ok.
119     ThreadMessageData data(message);
120
121     thread.threadMessages().add(&data);
122     bool result = thread.signal(toSystemSignal(signal));
123     if (!result)
124         return MessageStatus::ThreadExited;
125     RELEASE_ASSERT(result);
126
127     static StaticLock readLock;
128     while (true) {
129         LockHolder locker(readLock);
130         constexpr size_t bufferSize = 16;
131         char buffer[bufferSize];
132
133         // It's always safe to clear the pipe because only one thread can ever block trying to read
134         // from the pipe. Thus, each byte we clear from the pipe actually just corresponds to some task
135         // that has already finished. We actively want to ensure that the pipe does not overfill because
136         // otherwise our writers might spin trying to write.
137         auto clearPipe = [&] {
138             int flags = fcntl(fileDescriptors[Read], F_GETFL);
139             ASSERT(!(flags & O_NONBLOCK));
140             fcntl(fileDescriptors[Read], F_SETFL, flags | O_NONBLOCK);
141
142             while (read(fileDescriptors[Read], buffer, bufferSize) != -1) { }
143             ASSERT(errno == EAGAIN);
144
145             fcntl(fileDescriptors[Read], F_SETFL, flags);
146         };
147
148         if (Node* node = data.ran.load()) {
149             clearPipe();
150             delete node;
151             return MessageStatus::MessageRan;
152         }
153
154         int ret = read(fileDescriptors[Read], buffer, 1);
155         UNUSED_PARAM(ret);
156         ASSERT(buffer[0] == magicByte[0]);
157         clearPipe();
158     }
159     RELEASE_ASSERT_NOT_REACHED();
160 }
161
162 #if HAVE(MACH_EXCEPTIONS)
163 static MessageStatus sendMessageUsingMach(Thread& thread, const ThreadMessage& message)
164 {
165     static StaticLock messageLock;
166     auto lockholder = holdLock(messageLock);
167
168     auto result = thread.suspend();
169     if (!result)
170         return MessageStatus::ThreadExited;
171
172 #if CPU(X86)
173     unsigned userCount = sizeof(PlatformRegisters) / sizeof(int);
174     thread_state_flavor_t flavor = i386_THREAD_STATE;
175 #elif CPU(X86_64)
176     unsigned userCount = x86_THREAD_STATE64_COUNT;
177     thread_state_flavor_t flavor = x86_THREAD_STATE64;
178 #elif CPU(ARM)
179     unsigned userCount = ARM_THREAD_STATE_COUNT;
180     thread_state_flavor_t flavor = ARM_THREAD_STATE;
181 #elif CPU(ARM64)
182     unsigned userCount = ARM_THREAD_STATE64_COUNT;
183     thread_state_flavor_t flavor = ARM_THREAD_STATE64;
184 #else
185 #error Unknown Architecture
186 #endif
187
188     PlatformRegisters registers;
189     thread_state_t state = reinterpret_cast<thread_state_t>(&registers);
190     thread_get_state(thread.machThread(), flavor, state, &userCount);
191
192     message(registers);
193
194     thread_set_state(thread.machThread(), flavor, state, userCount);
195
196     thread.resume();
197     return MessageStatus::MessageRan;
198 }
199
200 static bool useMach = false;
201 void deliverMessagesUsingMach()
202 {
203     useMach = true;
204 }
205
206 #endif // HAVE(MACH_EXCEPTIONS)
207
208 MessageStatus sendMessageScoped(Thread& thread, const ThreadMessage& message)
209 {
210 #if HAVE(MACH_EXCEPTIONS)
211     if (useMach)
212         return sendMessageUsingMach(thread, message);
213 #endif
214     return sendMessageUsingSignal(thread, message);
215 }
216
217
218 } // namespace WTF
219
220 #endif // USE(PTHREADS)