diff --git a/common/widgets/progress_reporter.cpp b/common/widgets/progress_reporter.cpp
index c3a2d1e3a8..bf7b5f770e 100644
--- a/common/widgets/progress_reporter.cpp
+++ b/common/widgets/progress_reporter.cpp
@@ -30,7 +30,8 @@ PROGRESS_REPORTER::PROGRESS_REPORTER( int aNumPhases ) :
     m_phase( 0 ),
     m_numPhases( aNumPhases ),
     m_progress( 0 ),
-    m_maxProgress( 1 )
+    m_maxProgress( 1 ),
+    m_cancelled( false )
 {
 }
 
@@ -90,7 +91,10 @@ bool PROGRESS_REPORTER::KeepRefreshing( bool aWait )
         while( m_progress < m_maxProgress && m_maxProgress > 0 )
         {
             if( !updateUI() )
+            {
+                m_cancelled.store( true );
                 return false;
+            }
 
             wxMilliSleep( 20 );
         }
@@ -98,7 +102,13 @@ bool PROGRESS_REPORTER::KeepRefreshing( bool aWait )
     }
     else
     {
-        return updateUI();
+        if( !updateUI() )
+        {
+            m_cancelled.store( true );
+            return false;
+        }
+
+        return true;
     }
 }
 
diff --git a/include/widgets/progress_reporter.h b/include/widgets/progress_reporter.h
index 5bc099d9e1..696f7bbdd6 100644
--- a/include/widgets/progress_reporter.h
+++ b/include/widgets/progress_reporter.h
@@ -92,6 +92,7 @@ class PROGRESS_REPORTER
          */
         virtual void SetTitle( const wxString& aTitle ) {}
 
+        bool IsCancelled() const { return m_cancelled.load(); }
 
     protected:
 
@@ -105,6 +106,7 @@ class PROGRESS_REPORTER
         std::atomic_int    m_numPhases;
         std::atomic_int    m_progress;
         std::atomic_int    m_maxProgress;
+        std::atomic_bool   m_cancelled;
 };
 
 
diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp
index d173859fe2..5d93212e13 100644
--- a/pcbnew/zone_filler.cpp
+++ b/pcbnew/zone_filler.cpp
@@ -159,8 +159,8 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
     }
 
     std::atomic<size_t> nextItem( 0 );
-    size_t              parallelThreadCount =
-            std::min<size_t>( std::thread::hardware_concurrency(), aZones.size() );
+    size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
+                                                   aZones.size() );
     std::vector<std::future<size_t>> returns( parallelThreadCount );
 
     auto fill_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t
@@ -184,8 +184,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
             zone->SetIsFilled( true );
 
             if( m_progressReporter )
+            {
                 m_progressReporter->AdvanceProgress();
 
+                if( m_progressReporter->IsCancelled() )
+                    break;
+            }
+
             num++;
         }
 
@@ -206,8 +211,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
             do
             {
                 if( m_progressReporter )
+                {
                     m_progressReporter->KeepRefreshing();
 
+                    if( m_progressReporter->IsCancelled() )
+                        break;
+                }
+
                 status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
             } while( status != std::future_status::ready );
         }
@@ -219,11 +229,29 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
         m_progressReporter->AdvancePhase();
         m_progressReporter->Report( _( "Removing insulated copper islands..." ) );
         m_progressReporter->KeepRefreshing();
+
+        if( m_progressReporter->IsCancelled() )
+        {
+            if( m_commit )
+                m_commit->Revert();
+
+            connectivity->SetProgressReporter( nullptr );
+            return false;
+        }
     }
 
     connectivity->SetProgressReporter( m_progressReporter );
     connectivity->FindIsolatedCopperIslands( islandsList );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+    {
+        if( m_commit )
+            m_commit->Revert();
+
+        connectivity->SetProgressReporter( nullptr );
+        return false;
+    }
+
     // Now remove insulated copper islands and islands outside the board edge
     bool outOfDate = false;
 
@@ -285,6 +313,15 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
 
             if( aCheck && zone.m_zone->GetHashValue( layer ) != poly.GetHash() )
                 outOfDate = true;
+
+            if( m_progressReporter && m_progressReporter->IsCancelled() )
+            {
+                if( m_commit )
+                    m_commit->Revert();
+
+                connectivity->SetProgressReporter( nullptr );
+                return false;
+            }
         }
     }
 
@@ -314,7 +351,6 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
         m_progressReporter->SetMaxProgress( toFill.size() );
     }
 
-
     nextItem = 0;
 
     auto tri_lambda = [&] ( PROGRESS_REPORTER* aReporter ) -> size_t
@@ -327,7 +363,12 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
             num++;
 
             if( m_progressReporter )
+            {
                 m_progressReporter->AdvanceProgress();
+
+                if( m_progressReporter->IsCancelled() )
+                    break;
+            }
         }
 
         return num;
@@ -347,8 +388,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
             do
             {
                 if( m_progressReporter )
+                {
                     m_progressReporter->KeepRefreshing();
 
+                    if( m_progressReporter->IsCancelled() )
+                        break;
+                }
+
                 status = returns[ii].wait_for( std::chrono::milliseconds( 100 ) );
             } while( status != std::future_status::ready );
         }
@@ -359,6 +405,15 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
         m_progressReporter->AdvancePhase();
         m_progressReporter->Report( _( "Committing changes..." ) );
         m_progressReporter->KeepRefreshing();
+
+        if( m_progressReporter->IsCancelled() )
+        {
+            if( m_commit )
+                m_commit->Revert();
+
+            connectivity->SetProgressReporter( nullptr );
+            return false;
+        }
     }
 
     connectivity->SetProgressReporter( nullptr );
@@ -751,18 +806,30 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
     if( s_DumpZonesWhenFilling )
         dumper->BeginGroup( "clipper-zone" );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     knockoutThermalReliefs( aZone, aLayer, aRawPolys );
 
     if( s_DumpZonesWhenFilling )
         dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     buildCopperItemClearances( aZone, aLayer, clearanceHoles );
 
     if( s_DumpZonesWhenFilling )
         dumper->Write( &aRawPolys, "clearance holes" );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     buildThermalSpokes( aZone, aLayer, thermalSpokes );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     // Create a temporary zone that we can hit-test spoke-ends against.  It's only temporary
     // because the "real" subtract-clearance-holes has to be done after the spokes are added.
     static const bool USE_BBOX_CACHES = true;
@@ -776,9 +843,13 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
         testAreas.Inflate( half_min_width - epsilon, numSegs, intermediatecornerStrategy );
     }
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     // Spoke-end-testing is hugely expensive so we generate cached bounding-boxes to speed
     // things up a bit.
     testAreas.BuildBBoxCaches();
+    int interval = 0;
 
     for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes )
     {
@@ -791,6 +862,14 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
             continue;
         }
 
+        if( interval++ > 400 )
+        {
+            if( m_progressReporter && m_progressReporter->IsCancelled() )
+                return;
+
+            interval = 0;
+        }
+
         // Hit-test against other spokes
         for( const SHAPE_LINE_CHAIN& other : thermalSpokes )
         {
@@ -802,6 +881,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
         }
     }
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     // Ensure previous changes (adding thermal stubs) do not add
     // filled areas outside the zone boundary
     aRawPolys.BooleanIntersection( aSmoothedOutline, SHAPE_POLY_SET::PM_FAST );
@@ -810,6 +892,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
     if( s_DumpZonesWhenFilling )
         dumper->Write( &aRawPolys, "solid-areas-with-thermal-spokes" );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
     // Prune features that don't meet minimum-width criteria
     if( half_min_width - epsilon > epsilon )
@@ -818,6 +903,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
     if( s_DumpZonesWhenFilling )
         dumper->Write( &aRawPolys, "solid-areas-before-hatching" );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     // Now remove the non filled areas due to the hatch pattern
     if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
         addHatchFillTypeOnZone( aZone, aLayer, aRawPolys );
@@ -825,6 +913,9 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_I
     if( s_DumpZonesWhenFilling )
         dumper->Write( &aRawPolys, "solid-areas-after-hatching" );
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return;
+
     // Re-inflate after pruning of areas that don't meet minimum-width criteria
     if( aZone->GetFilledPolysUseThickness() )
     {
@@ -874,10 +965,13 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
     if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) )
         return false;
 
+    if( m_progressReporter && m_progressReporter->IsCancelled() )
+        return false;
+
     if( aZone->IsOnCopperLayer() )
     {
-        computeRawFilledArea(
-                aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys );
+        computeRawFilledArea( aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys,
+                              aFinalPolys );
     }
     else
     {