[iOS][wk2] Ensure that layers are marked volatile before allowing the process to...
authortimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Jun 2014 21:12:00 +0000 (21:12 +0000)
committertimothy_horton@apple.com <timothy_horton@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Jun 2014 21:12:00 +0000 (21:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=134004
<rdar://problem/17186342>

Reviewed by Simon Fraser.

WebKit tries to make layers volatile when unparented, but sometimes isn't given
a chance to do so before the process gets suspended, so we end up with lots of
non-volatile surfaces that should really be volatile.

* Shared/mac/RemoteLayerBackingStoreCollection.h:
* Shared/mac/RemoteLayerBackingStoreCollection.mm:
(WebKit::RemoteLayerBackingStoreCollection::markBackingStoreVolatileImmediately):
(WebKit::RemoteLayerBackingStoreCollection::markAllBackingStoreVolatileImmediatelyIfPossible):
Add markAllBackingStoreVolatileImmediatelyIfPossible, which tries to mark *all*
buffers of *all* backing store, (live and unreachable), (front, back, and secondary),
volatile right away. It returns false if any buffer isn't marked volatile (because it was in-use).

* UIProcess/ios/ProcessThrottler.h:
* UIProcess/ios/ProcessThrottler.mm:
(WebKit::ProcessThrottler::updateAssertion):
(WebKit::ProcessThrottler::processReadyToSuspend):
(WebKit::ProcessThrottler::didCancelProcessSuspension):
* UIProcess/ios/WebProcessProxyIOS.mm:
(WebKit::WebProcessProxy::sendCancelProcessWillSuspend):
(WebKit::WebProcessProxy::didCancelProcessSuspension):
* UIProcess/WebProcessProxy.h:
* UIProcess/WebProcessProxy.messages.in:
* WebProcess/WebProcess.h:
* WebProcess/WebProcess.messages.in:
If the UI process is waiting for the Web process to confirm that it can suspend
and something happens (the view is reparented) that cancels the suspension, inform
the Web process that this happened, so that it can cancel any cleanup that might still be taking place.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::viewStateDidChange):
If a view goes in-window, dispatch the view state change immediately without delay,
to minimize the latency between coming in-window and being ready to go.

* WebProcess/WebPage/DrawingArea.h:
(WebKit::DrawingArea::markLayersVolatileImmediatelyIfPossible):
* WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h:
* WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm:
(WebKit::RemoteLayerTreeDrawingArea::setRootCompositingLayer):
Schedule a flush when we change the root layer; otherwise, we can end up
detaching the root layer but changing nothing else, and never committing that change.

(WebKit::RemoteLayerTreeDrawingArea::markLayersVolatileImmediatelyIfPossible):

* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::WebProcess):
(WebKit::WebProcess::processWillSuspend):
(WebKit::WebProcess::cancelProcessWillSuspend):
(WebKit::WebProcess::markAllLayersVolatileIfPossible):
(WebKit::WebProcess::processSuspensionCleanupTimerFired):
When the UI process is going to suspend the process, it sends us ProcessWillSuspend,
and defers the suspension until we send a ProcessReadyToSuspend back.
Delay ProcessReadyToSuspend until all layers in our process have been marked volatile.
We'll keep trying every 20ms until they're all volatile. For safety, the UI process will eventually
stop waiting for us, but the volatility change is usually applied successfully within the first
or second timer callback.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@170316 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
Source/WebKit2/ChangeLog
Source/WebKit2/Shared/mac/RemoteLayerBackingStoreCollection.h
Source/WebKit2/Shared/mac/RemoteLayerBackingStoreCollection.mm
Source/WebKit2/UIProcess/WebPageProxy.cpp
Source/WebKit2/UIProcess/WebProcessProxy.h
Source/WebKit2/UIProcess/WebProcessProxy.messages.in
Source/WebKit2/UIProcess/ios/ProcessThrottler.h
Source/WebKit2/UIProcess/ios/ProcessThrottler.mm
Source/WebKit2/UIProcess/ios/WebProcessProxyIOS.mm
Source/WebKit2/WebProcess/WebPage/DrawingArea.h
Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h
Source/WebKit2/WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm
Source/WebKit2/WebProcess/WebProcess.cpp
Source/WebKit2/WebProcess/WebProcess.h
Source/WebKit2/WebProcess/WebProcess.messages.in

index ff22b0485edb2ea8fb7d28091734cb7f95c8a5c5..d428d66da0b1990ba96f53671c0182a4b4bb6397 100644 (file)
@@ -1,3 +1,67 @@
+2014-06-23  Timothy Horton  <timothy_horton@apple.com>
+
+        [iOS][wk2] Ensure that layers are marked volatile before allowing the process to suspend
+        https://bugs.webkit.org/show_bug.cgi?id=134004
+        <rdar://problem/17186342>
+
+        Reviewed by Simon Fraser.
+
+        WebKit tries to make layers volatile when unparented, but sometimes isn't given
+        a chance to do so before the process gets suspended, so we end up with lots of
+        non-volatile surfaces that should really be volatile.
+
+        * Shared/mac/RemoteLayerBackingStoreCollection.h:
+        * Shared/mac/RemoteLayerBackingStoreCollection.mm:
+        (WebKit::RemoteLayerBackingStoreCollection::markBackingStoreVolatileImmediately):
+        (WebKit::RemoteLayerBackingStoreCollection::markAllBackingStoreVolatileImmediatelyIfPossible):
+        Add markAllBackingStoreVolatileImmediatelyIfPossible, which tries to mark *all*
+        buffers of *all* backing store, (live and unreachable), (front, back, and secondary),
+        volatile right away. It returns false if any buffer isn't marked volatile (because it was in-use).
+
+        * UIProcess/ios/ProcessThrottler.h:
+        * UIProcess/ios/ProcessThrottler.mm:
+        (WebKit::ProcessThrottler::updateAssertion):
+        (WebKit::ProcessThrottler::processReadyToSuspend):
+        (WebKit::ProcessThrottler::didCancelProcessSuspension):
+        * UIProcess/ios/WebProcessProxyIOS.mm:
+        (WebKit::WebProcessProxy::sendCancelProcessWillSuspend):
+        (WebKit::WebProcessProxy::didCancelProcessSuspension):
+        * UIProcess/WebProcessProxy.h:
+        * UIProcess/WebProcessProxy.messages.in:
+        * WebProcess/WebProcess.h:
+        * WebProcess/WebProcess.messages.in:
+        If the UI process is waiting for the Web process to confirm that it can suspend
+        and something happens (the view is reparented) that cancels the suspension, inform
+        the Web process that this happened, so that it can cancel any cleanup that might still be taking place.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::viewStateDidChange):
+        If a view goes in-window, dispatch the view state change immediately without delay,
+        to minimize the latency between coming in-window and being ready to go.
+
+        * WebProcess/WebPage/DrawingArea.h:
+        (WebKit::DrawingArea::markLayersVolatileImmediatelyIfPossible):
+        * WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.h:
+        * WebProcess/WebPage/mac/RemoteLayerTreeDrawingArea.mm:
+        (WebKit::RemoteLayerTreeDrawingArea::setRootCompositingLayer):
+        Schedule a flush when we change the root layer; otherwise, we can end up
+        detaching the root layer but changing nothing else, and never committing that change.
+
+        (WebKit::RemoteLayerTreeDrawingArea::markLayersVolatileImmediatelyIfPossible):
+
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::WebProcess):
+        (WebKit::WebProcess::processWillSuspend):
+        (WebKit::WebProcess::cancelProcessWillSuspend):
+        (WebKit::WebProcess::markAllLayersVolatileIfPossible):
+        (WebKit::WebProcess::processSuspensionCleanupTimerFired):
+        When the UI process is going to suspend the process, it sends us ProcessWillSuspend,
+        and defers the suspension until we send a ProcessReadyToSuspend back.
+        Delay ProcessReadyToSuspend until all layers in our process have been marked volatile.
+        We'll keep trying every 20ms until they're all volatile. For safety, the UI process will eventually
+        stop waiting for us, but the volatility change is usually applied successfully within the first
+        or second timer callback.
+
 2014-06-23  Oliver Hunt  <oliver@apple.com>
 
         Ensure that we always use symlink free paths when specifying cache directories
index d223cb723ac2e4356c2c94a6691e274474d00b44..39d7a4df9463b18279e9cb90ad5cf69dc12d7bdb 100644 (file)
@@ -53,11 +53,16 @@ public:
     void didFlushLayers();
 
     void volatilityTimerFired(WebCore::Timer<RemoteLayerBackingStoreCollection>&);
+    bool markAllBackingStoreVolatileImmediatelyIfPossible();
 
     void scheduleVolatilityTimer();
 
 private:
-    bool markBackingStoreVolatileImmediately(RemoteLayerBackingStore&);
+    enum VolatilityMarkingFlag {
+        MarkBuffersIgnoringReachability = 1 << 0
+    };
+    typedef unsigned VolatilityMarkingFlags;
+    bool markBackingStoreVolatileImmediately(RemoteLayerBackingStore&, VolatilityMarkingFlags volatilityMarkingFlags = 0);
     bool markBackingStoreVolatile(RemoteLayerBackingStore&, std::chrono::steady_clock::time_point now);
 
     HashSet<RemoteLayerBackingStore*> m_liveBackingStore;
index 3e6646475741241404450cb97c7a426802fb3783..a464a9aab43ad3d9ea050b7c71f3653b2da8f9f5 100644 (file)
@@ -100,7 +100,7 @@ void RemoteLayerBackingStoreCollection::backingStoreWillBeDisplayed(RemoteLayerB
     m_unparentedBackingStore.remove(backingStoreIter);
 }
 
-bool RemoteLayerBackingStoreCollection::markBackingStoreVolatileImmediately(RemoteLayerBackingStore& backingStore)
+bool RemoteLayerBackingStoreCollection::markBackingStoreVolatileImmediately(RemoteLayerBackingStore& backingStore, VolatilityMarkingFlags volatilityMarkingFlags)
 {
     ASSERT(!m_inLayerFlush);
     bool successfullyMadeBackingStoreVolatile = true;
@@ -111,7 +111,7 @@ bool RemoteLayerBackingStoreCollection::markBackingStoreVolatileImmediately(Remo
     if (!backingStore.setBufferVolatility(RemoteLayerBackingStore::BufferType::Back, true))
         successfullyMadeBackingStoreVolatile = false;
 
-    if (!m_reachableBackingStoreInLatestFlush.contains(&backingStore)) {
+    if (!m_reachableBackingStoreInLatestFlush.contains(&backingStore) || (volatilityMarkingFlags & MarkBuffersIgnoringReachability)) {
         if (!backingStore.setBufferVolatility(RemoteLayerBackingStore::BufferType::Front, true))
             successfullyMadeBackingStoreVolatile = false;
     }
@@ -150,6 +150,19 @@ void RemoteLayerBackingStoreCollection::backingStoreBecameUnreachable(RemoteLaye
     markBackingStoreVolatileImmediately(backingStore);
 }
 
+bool RemoteLayerBackingStoreCollection::markAllBackingStoreVolatileImmediatelyIfPossible()
+{
+    bool successfullyMadeBackingStoreVolatile = true;
+
+    for (const auto& backingStore : m_liveBackingStore)
+        successfullyMadeBackingStoreVolatile &= markBackingStoreVolatileImmediately(*backingStore, MarkBuffersIgnoringReachability);
+
+    for (const auto& backingStore : m_unparentedBackingStore)
+        successfullyMadeBackingStoreVolatile &= markBackingStoreVolatileImmediately(*backingStore, MarkBuffersIgnoringReachability);
+
+    return successfullyMadeBackingStoreVolatile;
+}
+
 void RemoteLayerBackingStoreCollection::volatilityTimerFired(WebCore::Timer<RemoteLayerBackingStoreCollection>&)
 {
     bool successfullyMadeBackingStoreVolatile = true;
index e8e6c4a4f2b59688e547d25164c15de1ec412780..921565585e0113ba89342f4cd259c50721987092 100644 (file)
@@ -1106,6 +1106,11 @@ void WebPageProxy::viewStateDidChange(ViewState::Flags mayHaveChanged, WantsRepl
     m_viewStateChangeWantsReply = (wantsReply == WantsReplyOrNot::DoesWantReply || m_viewStateChangeWantsReply == WantsReplyOrNot::DoesWantReply) ? WantsReplyOrNot::DoesWantReply : WantsReplyOrNot::DoesNotWantReply;
 
 #if PLATFORM(COCOA)
+    bool isNewlyInWindow = !(m_viewState & ViewState::IsInWindow) && (mayHaveChanged & ViewState::IsInWindow) && m_pageClient.isViewInWindow();
+    if (isNewlyInWindow) {
+        dispatchViewStateChange();
+        return;
+    }
     m_viewStateChangeDispatcher->schedule();
 #else
     dispatchViewStateChange();
index d45ffc779424f73bde643b6e1d728c1b255de750..5eda9ca619fba2cde74e2b2b1c0150d2d8fb5c6c 100644 (file)
@@ -138,6 +138,8 @@ public:
 #if PLATFORM(IOS)
     void sendProcessWillSuspend();
     void processReadyToSuspend();
+    void sendCancelProcessWillSuspend();
+    void didCancelProcessSuspension();
     
     ProcessThrottler& throttler() { return *m_throttler; }
 #endif
index c54be3c12d50016d1532b0dce3802ace12cf20fe..eae30d886df7f4c0c1bb0e0940439464f5cc994f 100644 (file)
@@ -49,5 +49,6 @@ messages -> WebProcessProxy LegacyReceiver {
 #endif
 #if PLATFORM(IOS)
     ProcessReadyToSuspend()
+    DidCancelProcessSuspension()
 #endif
 }
index 738f69d0cc0af756bb778a9a46356cff7015521b..0461c274b0e655909dd9543ffc5788eb527d467f 100644 (file)
@@ -61,6 +61,7 @@ public:
     
     void didConnnectToProcess(pid_t);
     void processReadyToSuspend();
+    void didCancelProcessSuspension();
     
 private:
     friend class ForegroundActivityToken;
@@ -78,7 +79,7 @@ private:
     WebCore::Timer<ProcessThrottler> m_suspendTimer;
     unsigned m_foregroundCount;
     unsigned m_backgroundCount;
-    unsigned m_suspendMessageCount;
+    int m_suspendMessageCount;
 };
     
 }
index 8fc31d6e99912ec9855d918c2b095e39ad0a77d3..8e2e9b0e30a008975f653e85a47df3482736b062 100644 (file)
@@ -102,8 +102,14 @@ void ProcessThrottler::updateAssertion()
         m_process->sendProcessWillSuspend();
         m_suspendTimer.startOneShot(processSuspensionTimeout);
         m_assertion->setState(AssertionState::Background);
-    } else
-        updateAssertionNow();
+        return;
+    }
+
+    // If we're currently waiting for the Web process to do suspension cleanup, but no longer need to be suspended, tell the Web process to cancel the cleanup.
+    if (m_suspendTimer.isActive() && (m_foregroundCount || m_backgroundCount))
+        m_process->sendCancelProcessWillSuspend();
+
+    updateAssertionNow();
 }
 
 void ProcessThrottler::didConnnectToProcess(pid_t pid)
@@ -121,6 +127,14 @@ void ProcessThrottler::processReadyToSuspend()
 {
     if (!--m_suspendMessageCount)
         updateAssertionNow();
+    ASSERT(m_suspendMessageCount >= 0);
+}
+
+void ProcessThrottler::didCancelProcessSuspension()
+{
+    if (!--m_suspendMessageCount)
+        updateAssertionNow();
+    ASSERT(m_suspendMessageCount >= 0);
 }
 
 }
index 0f52697115bd22afc2bb0d102b83a37eb0b53e08..b960b61e5ee2b56351e8c5a3c52798b12fbc1929 100644 (file)
@@ -66,11 +66,22 @@ void WebProcessProxy::sendProcessWillSuspend()
     if (canSendMessage())
         send(Messages::WebProcess::ProcessWillSuspend(), 0);
 }
+
+void WebProcessProxy::sendCancelProcessWillSuspend()
+{
+    if (canSendMessage())
+        send(Messages::WebProcess::CancelProcessWillSuspend(), 0);
+}
     
 void WebProcessProxy::processReadyToSuspend()
 {
     m_throttler->processReadyToSuspend();
 }
+
+void WebProcessProxy::didCancelProcessSuspension()
+{
+    m_throttler->didCancelProcessSuspension();
+}
     
 } // namespace WebKit
 
index cef12a3006751ecb7a333a988659b4b9344c7210..f6b0d25eb8b9caec4db159cf10d04b38c3b1667f 100644 (file)
@@ -117,6 +117,8 @@ public:
     virtual void viewStateDidChange(WebCore::ViewState::Flags) { }
     virtual void setLayerHostingMode(LayerHostingMode) { }
 
+    virtual bool markLayersVolatileImmediatelyIfPossible() { return true; }
+
 protected:
     DrawingArea(DrawingAreaType, WebPage&);
 
index ab7d16e4c01bb691f8998adad01e9793105ce559..a2059ca5a3de4016e61205f1f7c0bd09881ab54f 100644 (file)
@@ -109,6 +109,8 @@ private:
 
     uint64_t takeNextTransactionID() { return ++m_currentTransactionID; }
 
+    virtual bool markLayersVolatileImmediatelyIfPossible() override;
+
     class BackingStoreFlusher : public ThreadSafeRefCounted<BackingStoreFlusher> {
     public:
         static PassRefPtr<BackingStoreFlusher> create(IPC::Connection*, std::unique_ptr<IPC::MessageEncoder>, Vector<RetainPtr<CGContextRef>>);
index 761714a5a637b2ba15c957338cb2e54b5a72afb0..cb8775e2450f7b80869f01846d738eb65340d7e7 100644 (file)
@@ -112,12 +112,14 @@ void RemoteLayerTreeDrawingArea::willDestroyDisplayRefreshMonitor(DisplayRefresh
 
 void RemoteLayerTreeDrawingArea::setRootCompositingLayer(GraphicsLayer* rootLayer)
 {
-    Vector<GraphicsLayer *> children;
+    Vector<GraphicsLayer*> children;
     if (rootLayer) {
         children.append(rootLayer);
         children.append(m_webPage.pageOverlayController().viewOverlayRootLayer());
     }
     m_rootLayer->setChildren(children);
+
+    scheduleCompositingLayerFlush();
 }
 
 void RemoteLayerTreeDrawingArea::updateGeometry(const IntSize& viewSize, const IntSize& layerPosition)
@@ -353,6 +355,11 @@ void RemoteLayerTreeDrawingArea::mainFrameContentSizeChanged(const IntSize& cont
     m_webPage.pageOverlayController().didChangeDocumentSize();
 }
 
+bool RemoteLayerTreeDrawingArea::markLayersVolatileImmediatelyIfPossible()
+{
+    return m_remoteLayerTreeContext->backingStoreCollection().markAllBackingStoreVolatileImmediatelyIfPossible();
+}
+
 PassRefPtr<RemoteLayerTreeDrawingArea::BackingStoreFlusher> RemoteLayerTreeDrawingArea::BackingStoreFlusher::create(IPC::Connection* connection, std::unique_ptr<IPC::MessageEncoder> encoder, Vector<RetainPtr<CGContextRef>> contextsToFlush)
 {
     return adoptRef(new RemoteLayerTreeDrawingArea::BackingStoreFlusher(connection, std::move(encoder), std::move(contextsToFlush)));
index 98798b712945ca529ada7954c784f7f3eb28abef..71e335b5350853c55670a9294c32daf896af63b8 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "APIFrameHandle.h"
 #include "AuthenticationManager.h"
+#include "DrawingArea.h"
 #include "EventDispatcher.h"
 #include "InjectedBundle.h"
 #include "InjectedBundleUserMessageCoders.h"
@@ -151,6 +152,7 @@ WebProcess::WebProcess()
     : m_eventDispatcher(EventDispatcher::create())
 #if PLATFORM(IOS)
     , m_viewUpdateDispatcher(ViewUpdateDispatcher::create())
+    , m_processSuspensionCleanupTimer(this, &WebProcess::processSuspensionCleanupTimerFired)
 #endif // PLATFORM(IOS)
     , m_inDidClose(false)
     , m_hasSetCacheModel(false)
@@ -1163,9 +1165,42 @@ void WebProcess::resetAllGeolocationPermissions()
 void WebProcess::processWillSuspend()
 {
     memoryPressureHandler().releaseMemory(true);
-    parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(), 0);
+
+    if (!markAllLayersVolatileIfPossible())
+        m_processSuspensionCleanupTimer.startRepeating(std::chrono::milliseconds(20));
+    else
+        parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(), 0);
+}
+
+void WebProcess::cancelProcessWillSuspend()
+{
+    // If we've already finished cleaning up and sent ProcessReadyToSuspend, we
+    // shouldn't send DidCancelProcessSuspension; the UI process strictly expects one or the other.
+    if (!m_processSuspensionCleanupTimer.isActive())
+        return;
+
+    m_processSuspensionCleanupTimer.stop();
+    parentProcessConnection()->send(Messages::WebProcessProxy::DidCancelProcessSuspension(), 0);
+}
+
+bool WebProcess::markAllLayersVolatileIfPossible()
+{
+    bool successfullyMarkedAllLayersVolatile = true;
+    for (auto& page : m_pageMap.values()) {
+        if (auto drawingArea = page->drawingArea())
+            successfullyMarkedAllLayersVolatile &= drawingArea->markLayersVolatileImmediatelyIfPossible();
+    }
+
+    return successfullyMarkedAllLayersVolatile;
+}
+
+void WebProcess::processSuspensionCleanupTimerFired(Timer<WebProcess>* timer)
+{
+    if (markAllLayersVolatileIfPossible()) {
+        parentProcessConnection()->send(Messages::WebProcessProxy::ProcessReadyToSuspend(), 0);
+        timer->stop();
+    }
 }
-    
 #endif // PLATFORM(IOS)
 
 void WebProcess::pageDidEnterWindow(uint64_t pageID)
index 06b7bf9b9c50d9161e9fc4783fff6443112bfe8c..30955df19059c2c380ea8a09a099b2ec0c834427 100644 (file)
@@ -29,6 +29,7 @@
 #include "CacheModel.h"
 #include "ChildProcess.h"
 #include "DownloadManager.h"
+#include "DrawingArea.h"
 #include "PluginProcessConnectionManager.h"
 #include "ResourceCachesToClear.h"
 #include "SandboxExtension.h"
@@ -177,6 +178,9 @@ public:
 #if PLATFORM(IOS)
     void resetAllGeolocationPermissions();
     void processWillSuspend();
+    void cancelProcessWillSuspend();
+    bool markAllLayersVolatileIfPossible();
+    void processSuspensionCleanupTimerFired(WebCore::Timer<WebProcess>*);
 #endif // PLATFORM(IOS)
 
     RefPtr<API::Object> apiObjectByConvertingFromHandles(API::Object*);
@@ -286,6 +290,7 @@ private:
     RefPtr<EventDispatcher> m_eventDispatcher;
 #if PLATFORM(IOS)
     RefPtr<ViewUpdateDispatcher> m_viewUpdateDispatcher;
+    WebCore::Timer<WebProcess> m_processSuspensionCleanupTimer;
 #endif
 
     bool m_inDidClose;
index 63fee0d5a685ef1fc5cc9f6f119a717713972a1f..8f77e5ea46ac99547cafa60e364b702b0e731031 100644 (file)
@@ -95,5 +95,6 @@ messages -> WebProcess LegacyReceiver {
 
 #if PLATFORM(IOS)
     ProcessWillSuspend()
+    CancelProcessWillSuspend()
 #endif
 }