4343import hudson .security .ACLContext ;
4444import hudson .slaves .ComputerListener ;
4545import hudson .slaves .WorkspaceList ;
46+ import hudson .util .ClassLoaderSanityThreadFactory ;
47+ import hudson .util .DaemonThreadFactory ;
48+ import hudson .util .ExceptionCatchingThreadFactory ;
49+ import hudson .util .NamingThreadFactory ;
4650import hudson .util .TextFile ;
4751import java .io .BufferedReader ;
4852import java .io .File ;
5357import java .nio .charset .StandardCharsets ;
5458import java .security .MessageDigest ;
5559import java .security .NoSuchAlgorithmException ;
56- import java .util .concurrent .Semaphore ;
60+ import java .util .concurrent .ExecutorService ;
5761import java .util .Iterator ;
5862import java .util .LinkedList ;
5963import java .util .List ;
6064import java .util .Map ;
6165import java .util .Queue ;
6266import java .util .TreeMap ;
6367import java .util .WeakHashMap ;
68+ import java .util .concurrent .LinkedBlockingQueue ;
69+ import java .util .concurrent .SynchronousQueue ;
70+ import java .util .concurrent .ThreadPoolExecutor ;
6471import java .util .concurrent .TimeUnit ;
6572import java .util .logging .Level ;
6673import java .util .logging .Logger ;
6774import java .util .regex .Matcher ;
6875import java .util .regex .Pattern ;
76+ import java .util .stream .Collectors ;
77+ import java .util .stream .Stream ;
6978import edu .umd .cs .findbugs .annotations .CheckForNull ;
7079import jenkins .MasterToSlaveFileCallable ;
7180import jenkins .model .Jenkins ;
81+ import jenkins .security .ImpersonatingExecutorService ;
7282import jenkins .slaves .WorkspaceLocator ;
83+ import jenkins .util .ContextResettingExecutorService ;
84+ import jenkins .util .ErrorLoggingExecutorService ;
7385import jenkins .util .SystemProperties ;
7486import org .apache .commons .codec .binary .Base32 ;
7587import org .apache .commons .lang .StringUtils ;
@@ -380,24 +392,49 @@ static String minimize(String name) {
380392 @ Extension
381393 public static class Deleter extends ItemListener {
382394
383- private static /* almost final */ int CLEANUP_THREAD_LIMIT = SystemProperties .getInteger (Deleter .class .getName () + ".CLEANUP_THREAD_LIMIT" , Integer . valueOf ( 0 )). intValue ( );
395+ private static final int CLEANUP_THREAD_LIMIT = SystemProperties .getInteger (Deleter .class .getName () + ".CLEANUP_THREAD_LIMIT" , 0 );
384396
385- /** Semaphore for limiting number of scheduled {@link CleanupTask} */
386- private static Semaphore cleanupPool = new Semaphore (CLEANUP_THREAD_LIMIT , true );
397+ private static final ExecutorService executorService = executorService ();
387398
388399 /** Number of {@link CleanupTask} which have been scheduled but not yet completed. */
389400 private static int runningTasks ;
390401
402+ private static ExecutorService executorService () {
403+ if (CLEANUP_THREAD_LIMIT > 0 ) {
404+ ThreadPoolExecutor tpe = new ThreadPoolExecutor (CLEANUP_THREAD_LIMIT , CLEANUP_THREAD_LIMIT , 10L , TimeUnit .SECONDS , new LinkedBlockingQueue <>(),
405+ new ExceptionCatchingThreadFactory (
406+ new NamingThreadFactory (
407+ new ClassLoaderSanityThreadFactory (new DaemonThreadFactory ()),
408+ "Deleter.cleanupTask" )));
409+ // Use allowCoreThreadTimeOut to keep a lightweight ThreadPoolExecutor. The Thread pool grows to the
410+ // limit and then queue tasks. Thread will then be removed when idle
411+ tpe .allowCoreThreadTimeOut (true );
412+ return new ContextResettingExecutorService (
413+ new ImpersonatingExecutorService (
414+ new ErrorLoggingExecutorService (tpe ),
415+ ACL .SYSTEM2 ));
416+ } else {
417+ return Computer .threadPoolForRemoting ;
418+ }
419+ }
420+
391421 @ Override
392422 public void onDeleted (Item item ) {
393423 if (!(item instanceof TopLevelItem )) {
394424 return ;
395425 }
396426 TopLevelItem tli = (TopLevelItem ) item ;
397427 Jenkins jenkins = Jenkins .get ();
398- Computer .threadPoolForRemoting .submit (new CleanupTask (tli , jenkins ));
399- // Starts provisioner Thread which is tasked with starting cleanup Threads
400- new CleanupTaskProvisioner (tli , jenkins .getNodes ()).run ();
428+ Queue <Node > nodes = Stream
429+ .concat (Stream .of (jenkins ), jenkins .getNodes ().stream ())
430+ .collect (Collectors .toCollection (LinkedList ::new ));
431+ try {
432+ while (!nodes .isEmpty ()){
433+ executorService .execute (new CleanupTask (tli , nodes .remove ()));
434+ }
435+ } catch (Exception e ) {
436+ LOGGER .log (Level .WARNING , e .getMessage ());
437+ }
401438 }
402439
403440 @ Override
@@ -412,20 +449,6 @@ public void onLocationChanged(Item item, String oldFullName, String newFullName)
412449 }
413450 }
414451
415- public void acquireThread () throws InterruptedException {
416- if (CLEANUP_THREAD_LIMIT <= 0 ) {
417- return ;
418- }
419- cleanupPool .acquire ();
420- }
421-
422- public void releaseThread () {
423- if (CLEANUP_THREAD_LIMIT <= 0 ) {
424- return ;
425- }
426- cleanupPool .release ();
427- }
428-
429452 // Visible for testing
430453 static synchronized void waitForTasksToFinish () throws InterruptedException {
431454 while (runningTasks > 0 ) {
@@ -442,36 +465,6 @@ private static synchronized void taskFinished() {
442465 Deleter .class .notifyAll ();
443466 }
444467
445- private static class CleanupTaskProvisioner implements Runnable {
446-
447- @ NonNull
448- private final TopLevelItem tli ;
449-
450- @ NonNull
451- private final Queue <Node > nodes ;
452-
453- @ NonNull
454- private final Deleter deleter ;
455-
456- public CleanupTaskProvisioner (TopLevelItem tli , List <Node > nodes ) {
457- this .tli = tli ;
458- this .nodes = new LinkedList <>(nodes );
459- this .deleter = ExtensionList .lookupSingleton (Deleter .class );
460- }
461-
462- @ Override
463- public void run () {
464- try {
465- while (!nodes .isEmpty ()){
466- deleter .acquireThread ();
467- Computer .threadPoolForRemoting .submit (new CleanupTask (tli , nodes .remove ()));
468- }
469- } catch (Exception e ) {
470- LOGGER .log (Level .WARNING , e .getMessage ());
471- }
472- }
473- }
474-
475468 private static class CleanupTask implements Runnable {
476469
477470 @ NonNull
@@ -480,13 +473,9 @@ private static class CleanupTask implements Runnable {
480473 @ NonNull
481474 private final Node node ;
482475
483- @ NonNull
484- private final Deleter deleter ;
485-
486476 CleanupTask (TopLevelItem tli , Node node ) {
487477 this .tli = tli ;
488478 this .node = node ;
489- this .deleter = ExtensionList .lookupSingleton (Deleter .class );
490479 taskStarted ();
491480 }
492481
@@ -529,7 +518,6 @@ public void run() {
529518 }
530519 } finally {
531520 t .setName (oldName );
532- deleter .releaseThread ();
533521 taskFinished ();
534522 }
535523 }
0 commit comments