Changeset 233111 in webkit
- Timestamp:
- Jun 22, 2018, 5:09:37 PM (8 years ago)
- Location:
- trunk/Source/WebKit
- Files:
-
- 5 edited
-
ChangeLog (modified) (1 diff)
-
Platform/IPC/Connection.cpp (modified) (5 diffs)
-
Platform/IPC/Connection.h (modified) (7 diffs)
-
Platform/IPC/mac/ConnectionMac.mm (modified) (1 diff)
-
UIProcess/WebProcessProxy.cpp (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit/ChangeLog
r233108 r233111 1 2018-06-22 Chris Dumez <cdumez@apple.com> 2 3 Implement IPC throttling to keep the main thread responsive when a process misbehaves 4 https://bugs.webkit.org/show_bug.cgi?id=186607 5 <rdar://problem/41073205> 6 7 Reviewed by Geoff Garen and Brady Eidson. 8 9 Implement IPC throttling to keep the main thread responsive when a process misbehaves. 10 Instead of doing one main runloop dispatch per incoming message, we now do a single 11 runloop dispatch and process incoming messages in batch. We put a limit on the number 12 of messages to be processed in a batch (600). If the queue is larger that this limit, 13 we'll schedule a 0-timer to process remaining messages, giving the main runloop a chance 14 to process other events. Additionally, if an IPC connection keeps hitting this maximum 15 batch size limit, we implement back off and we'll further decrease the number of messages 16 we process in each batch (going as low as 60). This keeps Safari responsive enough to 17 allow the user to close the bad tab (even on older devices such as iPhone 5s). 18 19 Finally, if the incoming message queue becomes too large (50000), we go one step further 20 and kill the IPC connection in order to maintain performance / battery life. 21 22 Every time we apply throttling or terminate a connection due to throttling, we do a 23 RELEASE_LOG_ERROR() with useful information in order to help diagnose potential issues 24 in the future. 25 26 For now, incoming IPC messages throttling is only enabled on the UIProcess' connections 27 to the WebProcesses. 28 29 * Platform/IPC/Connection.cpp: 30 (IPC::Connection::Connection): 31 (IPC::Connection::enqueueIncomingMessage): 32 (IPC::Connection::MessagesThrottler::MessagesThrottler): 33 (IPC::Connection::MessagesThrottler::scheduleMessagesDispatch): 34 (IPC::Connection::MessagesThrottler::numberOfMessagesToProcess): 35 (IPC::Connection::dispatchIncomingMessages): 36 * Platform/IPC/Connection.h: 37 * Platform/IPC/mac/ConnectionMac.mm: 38 (IPC::Connection::kill): 39 1 40 2018-06-22 Sihui Liu <sihui_liu@apple.com> 2 41 -
trunk/Source/WebKit/Platform/IPC/Connection.cpp
r233068 r233111 45 45 namespace IPC { 46 46 47 #if PLATFORM(COCOA) 48 // The IPC connection gets killed if the incoming message queue reaches 50000 messages before the main thread has a chance to dispatch them. 49 const size_t maxPendingIncomingMessagesKillingThreshold { 50000 }; 50 #endif 51 47 52 struct Connection::ReplyHandler { 48 53 RefPtr<FunctionDispatcher> dispatcher; … … 755 760 } 756 761 762 void Connection::enableIncomingMessagesThrottling() 763 { 764 if (m_incomingMessagesThrottler) 765 return; 766 767 m_incomingMessagesThrottler = std::make_unique<MessagesThrottler>(*this, &Connection::dispatchIncomingMessages); 768 } 769 757 770 void Connection::postConnectionDidCloseOnConnectionWorkQueue() 758 771 { … … 894 907 { 895 908 std::lock_guard<Lock> lock(m_incomingMessagesMutex); 909 910 #if PLATFORM(COCOA) 911 if (m_wasKilled) 912 return; 913 914 if (m_incomingMessages.size() >= maxPendingIncomingMessagesKillingThreshold) { 915 if (kill()) { 916 RELEASE_LOG_ERROR(IPC, "%p - Connection::enqueueIncomingMessage: Over %zu incoming messages have been queued without the main thread processing them, killing the connection as the remote process seems to be misbehaving", this, maxPendingIncomingMessagesKillingThreshold); 917 m_incomingMessages.clear(); 918 } 919 return; 920 } 921 #endif 922 896 923 m_incomingMessages.append(WTFMove(incomingMessage)); 924 925 if (m_incomingMessagesThrottler && m_incomingMessages.size() != 1) 926 return; 897 927 } 898 928 899 929 RunLoop::main().dispatch([protectedThis = makeRef(*this)]() mutable { 900 protectedThis->dispatchOneMessage(); 930 if (protectedThis->m_incomingMessagesThrottler) 931 protectedThis->dispatchIncomingMessages(); 932 else 933 protectedThis->dispatchOneIncomingMessage(); 901 934 }); 902 935 } … … 950 983 } 951 984 952 void Connection::dispatchOneMessage() 985 Connection::MessagesThrottler::MessagesThrottler(Connection& connection, DispatchMessagesFunction dispatchMessages) 986 : m_dispatchMessagesTimer(RunLoop::main(), &connection, dispatchMessages) 987 , m_connection(connection) 988 , m_dispatchMessages(dispatchMessages) 989 { 990 ASSERT(RunLoop::isMain()); 991 } 992 993 void Connection::MessagesThrottler::scheduleMessagesDispatch() 994 { 995 ASSERT(RunLoop::isMain()); 996 997 if (m_throttlingLevel) { 998 m_dispatchMessagesTimer.startOneShot(0_s); 999 return; 1000 } 1001 RunLoop::main().dispatch([this, protectedConnection = makeRefPtr(&m_connection)]() mutable { 1002 (protectedConnection.get()->*m_dispatchMessages)(); 1003 }); 1004 } 1005 1006 size_t Connection::MessagesThrottler::numberOfMessagesToProcess(size_t totalMessages) 1007 { 1008 ASSERT(RunLoop::isMain()); 1009 1010 // Never dispatch more than 600 messages without returning to the run loop, we can go as low as 60 with maximum throttling level. 1011 static const size_t maxIncomingMessagesDispatchingBatchSize { 600 }; 1012 static const unsigned maxThrottlingLevel = 9; 1013 1014 size_t batchSize = maxIncomingMessagesDispatchingBatchSize / (m_throttlingLevel + 1); 1015 1016 if (totalMessages > maxIncomingMessagesDispatchingBatchSize) 1017 m_throttlingLevel = std::min(m_throttlingLevel + 1, maxThrottlingLevel); 1018 else if (m_throttlingLevel) 1019 --m_throttlingLevel; 1020 1021 return std::min(totalMessages, batchSize); 1022 } 1023 1024 void Connection::dispatchOneIncomingMessage() 953 1025 { 954 1026 std::unique_ptr<Decoder> message; 955 956 1027 { 957 1028 std::lock_guard<Lock> lock(m_incomingMessagesMutex); … … 965 1036 } 966 1037 1038 void Connection::dispatchIncomingMessages() 1039 { 1040 ASSERT(RunLoop::isMain()); 1041 1042 std::unique_ptr<Decoder> message; 1043 1044 size_t messagesToProcess = 0; 1045 { 1046 std::lock_guard<Lock> lock(m_incomingMessagesMutex); 1047 if (m_incomingMessages.isEmpty()) 1048 return; 1049 1050 message = m_incomingMessages.takeFirst(); 1051 1052 // Incoming messages may get adding to the queue by the IPC thread while we're dispatching the messages below. 1053 // To make sure dispatchIncomingMessages() yields, we only ever process messages that were in the queue when 1054 // dispatchIncomingMessages() was called. Additionally, the MessageThrottler may further cap the number of 1055 // messages to process to make sure we give the main run loop a chance to process other events. 1056 messagesToProcess = m_incomingMessagesThrottler->numberOfMessagesToProcess(m_incomingMessages.size()); 1057 if (messagesToProcess < m_incomingMessages.size()) { 1058 RELEASE_LOG_ERROR(IPC, "%p - Connection::dispatchIncomingMessages: IPC throttling was triggered (has %zu pending incoming messages, will only process %zu before yielding)", this, m_incomingMessages.size(), messagesToProcess); 1059 #if PLATFORM(COCOA) 1060 RELEASE_LOG_ERROR(IPC, "%p - Connection::dispatchIncomingMessages: first IPC message in queue is %{public}s::%{public}s", this, message->messageReceiverName().toString().data(), message->messageName().toString().data()); 1061 #endif 1062 } 1063 1064 // Re-schedule ourselves *before* we dispatch the messages because we want to process follow-up messages if the client 1065 // spins a nested run loop while we're dispatching a message. Note that this means we can re-enter this method. 1066 if (!m_incomingMessages.isEmpty()) 1067 m_incomingMessagesThrottler->scheduleMessagesDispatch(); 1068 } 1069 1070 dispatchMessage(WTFMove(message)); 1071 1072 for (size_t i = 1; i < messagesToProcess; ++i) { 1073 { 1074 std::lock_guard<Lock> lock(m_incomingMessagesMutex); 1075 if (m_incomingMessages.isEmpty()) 1076 return; 1077 1078 message = m_incomingMessages.takeFirst(); 1079 } 1080 dispatchMessage(WTFMove(message)); 1081 } 1082 } 1083 967 1084 void Connection::wakeUpRunLoop() 968 1085 { -
trunk/Source/WebKit/Platform/IPC/Connection.h
r233068 r233111 41 41 #include <wtf/Lock.h> 42 42 #include <wtf/OptionSet.h> 43 #include <wtf/RunLoop.h> 43 44 #include <wtf/WorkQueue.h> 44 45 #include <wtf/text/CString.h> … … 210 211 void ignoreTimeoutsForTesting() { m_ignoreTimeoutsForTesting = true; } 211 212 213 void enableIncomingMessagesThrottling(); 214 212 215 private: 213 216 Connection(Identifier, bool isServer, Client&); … … 232 235 233 236 // Called on the listener thread. 234 void dispatchOneMessage(); 237 void dispatchOneIncomingMessage(); 238 void dispatchIncomingMessages(); 235 239 void dispatchMessage(std::unique_ptr<Decoder>); 236 240 void dispatchMessage(Decoder&); … … 241 245 // Can be called on any thread. 242 246 void enqueueIncomingMessage(std::unique_ptr<Decoder>); 247 size_t incomingMessagesDispatchingBatchSize() const; 243 248 244 249 void willSendSyncMessage(OptionSet<SendSyncOption>); … … 250 255 bool sendMessage(std::unique_ptr<MachMessage>); 251 256 #endif 257 258 class MessagesThrottler { 259 public: 260 typedef void (Connection::*DispatchMessagesFunction)(); 261 MessagesThrottler(Connection&, DispatchMessagesFunction); 262 263 size_t numberOfMessagesToProcess(size_t totalMessages); 264 void scheduleMessagesDispatch(); 265 266 private: 267 RunLoop::Timer<Connection> m_dispatchMessagesTimer; 268 Connection& m_connection; 269 DispatchMessagesFunction m_dispatchMessages; 270 unsigned m_throttlingLevel { 0 }; 271 }; 252 272 253 273 Client& m_client; … … 276 296 Lock m_incomingMessagesMutex; 277 297 Deque<std::unique_ptr<Decoder>> m_incomingMessages; 298 std::unique_ptr<MessagesThrottler> m_incomingMessagesThrottler; 278 299 279 300 // Outgoing messages. … … 340 361 341 362 OSObjectPtr<xpc_connection_t> m_xpcConnection; 363 bool m_wasKilled { false }; 342 364 #elif OS(WINDOWS) 343 365 // Called on the connection queue. -
trunk/Source/WebKit/Platform/IPC/mac/ConnectionMac.mm
r233068 r233111 624 624 if (m_xpcConnection) { 625 625 xpc_connection_kill(m_xpcConnection.get(), SIGKILL); 626 m_wasKilled = true; 626 627 return true; 627 628 } -
trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp
r232815 r233111 186 186 ASSERT(this->connection() == &connection); 187 187 188 // Throttling IPC messages coming from the WebProcesses so that the UIProcess stays responsive, even 189 // if one of the WebProcesses misbehaves. 190 connection.enableIncomingMessagesThrottling(); 191 188 192 #if ENABLE(SEC_ITEM_SHIM) 189 193 SecItemShimProxy::singleton().initializeConnection(connection);
Note:
See TracChangeset
for help on using the changeset viewer.