@@ -21,6 +21,7 @@ import java.util.Collections
2121
2222import scala .collection .JavaConverters ._
2323import scala .collection .mutable .ArrayBuffer
24+ import scala .reflect .ClassTag
2425
2526import org .apache .mesos .{Protos , Scheduler , SchedulerDriver }
2627import org .apache .mesos .Protos ._
@@ -34,6 +35,7 @@ import org.scalatest.BeforeAndAfter
3435import org .apache .spark .{LocalSparkContext , SecurityManager , SparkConf , SparkContext , SparkFunSuite }
3536import org .apache .spark .network .shuffle .mesos .MesosExternalShuffleClient
3637import org .apache .spark .rpc .RpcEndpointRef
38+ import org .apache .spark .scheduler .cluster .CoarseGrainedClusterMessages .RemoveExecutor
3739import org .apache .spark .scheduler .TaskSchedulerImpl
3840
3941class MesosCoarseGrainedSchedulerBackendSuite extends SparkFunSuite
@@ -47,6 +49,7 @@ class MesosCoarseGrainedSchedulerBackendSuite extends SparkFunSuite
4749 private var backend : MesosCoarseGrainedSchedulerBackend = _
4850 private var externalShuffleClient : MesosExternalShuffleClient = _
4951 private var driverEndpoint : RpcEndpointRef = _
52+ @ volatile private var stopCalled = false
5053
5154 test(" mesos supports killing and limiting executors" ) {
5255 setBackend()
@@ -252,6 +255,32 @@ class MesosCoarseGrainedSchedulerBackendSuite extends SparkFunSuite
252255 backend.start()
253256 }
254257
258+ test(" Do not call removeExecutor() after backend is stopped" ) {
259+ setBackend()
260+
261+ // launches a task on a valid offer
262+ val offers = List ((backend.executorMemory(sc), 1 ))
263+ offerResources(offers)
264+ verifyTaskLaunched(" o1" )
265+
266+ // launches a thread simulating status update
267+ val statusUpdateThread = new Thread {
268+ override def run (): Unit = {
269+ while (! stopCalled) {
270+ Thread .sleep(100 )
271+ }
272+
273+ val status = createTaskStatus(" 0" , " s1" , TaskState .TASK_FINISHED )
274+ backend.statusUpdate(driver, status)
275+ }
276+ }.start
277+
278+ backend.stop()
279+ // Any method of the backend involving sending messages to the driver endpoint should not
280+ // be called after the backend is stopped.
281+ verify(driverEndpoint, never()).askWithRetry(isA(classOf [RemoveExecutor ]))(any[ClassTag [_]])
282+ }
283+
255284 private def verifyDeclinedOffer (driver : SchedulerDriver ,
256285 offerId : OfferID ,
257286 filter : Boolean = false ): Unit = {
@@ -350,6 +379,10 @@ class MesosCoarseGrainedSchedulerBackendSuite extends SparkFunSuite
350379 mesosDriver = newDriver
351380 }
352381
382+ override def stopExecutors (): Unit = {
383+ stopCalled = true
384+ }
385+
353386 markRegistered()
354387 }
355388 backend.start()
0 commit comments