2010-08-30 3 views
16

Под менеджером безопасности по умолчанию, если создать ExecutorService (ThreadPoolExecutor в данном случае), я не могу закрыть его, shutdown() просто вызывает checkPermission("modifyThread") и, таким образом, сразу умирает:Почему я не могу отключить собственный ExecutorService под SecurityManager?

import java.util.concurrent.*; 

class A { 
    public static void main(String[] args) { 
     Thread ct = Thread.currentThread(); 
     System.out.println("current thread: " + ct); 
     ct.checkAccess(); // we have access to our own thread... 
     ThreadPoolExecutor tpe = new ThreadPoolExecutor(
      1, // one core thread 
      1, // doesn't matter because queue is unbounded 
      0, TimeUnit.SECONDS, // doesn't matter in this case 
      new LinkedBlockingQueue<Runnable>(), /* unbound queue for 
                * our single thread */ 
      new ThreadFactory() { 
       public Thread newThread(Runnable r) { 
        // obviously never gets called as we don't add any work 
        System.out.println("making thread"); 
        return new Thread(r); 
       } 
      } 
     ); 
     tpe.shutdown(); // raises security exception 
    } 
} 

Sun JDK:

$ java -Djava.security.manager A Текущая тема: Тема [main, 5, main] Исключение в потоке «main» java.security.AccessControlException: доступ запрещен (java.lang.RuntimePermission modifyThread) по адресуjava.security.AccessControlContext.checkPermission (AccessControlContext.java:323) на java.security.AccessController.checkPermission (AccessController.java:546) на java.lang.SecurityManager.checkPermission (SecurityManager.java:532) в java.util.concurrent.ThreadPoolExecutor.shutdown (ThreadPoolExecutor.java:1094) в A.main (A.java:22)

OpenJDK:

$ Java -Djava.security .manager A Текущая тема: Тема [main, 5, main] Исключение в теме "main" java.security.AccessControlException: доступ запрещен (java.lang.RuntimePermission modifyThread) at java.security.AccessControlContext.checkPermission (AccessControlContext.java:342) на java.security.AccessController.checkPermission (AccessController.java:553) в java.lang.SecurityManager.checkPermission (SecurityManager.java:549) в java.util.concurrent.ThreadPoolExecutor.checkShutdownAccess (ThreadPoolExecutor.java:711) на java.util.concurrent.ThreadPoolExecutor.shutdown (ThreadPoolExecutor.java:1351) на A.main (A.java:22)

Почему ??????? Что такое - это последствия безопасности для создания пула потоков, который только у вас управления и отключения его? Является ли это ошибкой в ​​реализации, или я чего-то не хватает?

Давайте посмотрим, что спецификации для ExecutorService.shutdown говорит ...

Инициирует процедуру завершения работы, в котором выполняются ранее представленные задачи, но никакие новые задачи не будут приняты. Вызов не имеет дополнительного эффекта, если он уже выключен.

Броски: SecurityException - если менеджер безопасности существует и закрытие этого ExecutorService может манипулировать потоков, которые абонент не разрешается изменять, поскольку он не имеет RuntimePermission («modifyThread»), или метод CheckAccess менеджер по безопасности отрицает доступ ,

Это ... примерно такое же неопределенное, как и оно.Спектр говорит ничего о любых «системных потоках», которые были сделаны в течение жизненного цикла ExecutorService, и, кроме того, он позволяет вам создавать свои собственные темы, что является доказательством того, что должно быть нет «системные потоки», связанные с Вы делаете это. (Как сделано выше в моем источнике выборки)

Похоже, что разработчики Java SE увидели, что можно сделать shutdown, чтобы поднять SecurityException, поэтому они были такими же: «О, хорошо, я просто добавлю случайную проверку безопасности здесь соблюдение»...

дело в том, перечитывая OpenJDK источника (OpenJDK-6-Src-b20-21_jun_2010), то получается, что только способ любой нить когда-либо созданных, это, позвонив ThreadFactory (который никогда не вызывается в моем тестовом файле, так как я не создаю никакой работы, и я не звоню prestartCoreThread или preStartAllCoreThreads). Проверка безопасности, таким образом, делается без всякой видимой причины в ThreadPoolExecutor OpenJDK (как это делается в ВС-JDK-1,6, но у меня нет источника):

/** 
* Initiates an orderly shutdown in which previously submitted 
* tasks are executed, but no new tasks will be accepted. 
* Invocation has no additional effect if already shut down. 
* 
* @throws SecurityException {@inheritDoc} 
*/ 
public void shutdown() { 
    final ReentrantLock mainLock = this.mainLock; 
    mainLock.lock(); 
    try { 
     checkShutdownAccess(); 
     advanceRunState(SHUTDOWN); 
     interruptIdleWorkers(); 
     onShutdown(); // hook for ScheduledThreadPoolExecutor 
    } finally { 
     mainLock.unlock(); 
    } 
    tryTerminate(); 
} 

checkShutdownAccess называется, прежде чем делать что-нибудь ...

/** 
* If there is a security manager, makes sure caller has 
* permission to shut down threads in general (see shutdownPerm). 
* If this passes, additionally makes sure the caller is allowed 
* to interrupt each worker thread. This might not be true even if 
* first check passed, if the SecurityManager treats some threads 
* specially. 
*/ 
private void checkShutdownAccess() { 
    SecurityManager security = System.getSecurityManager(); 
    if (security != null) { 
     security.checkPermission(shutdownPerm); 
     final ReentrantLock mainLock = this.mainLock; 
     mainLock.lock(); 
     try { 
      for (Worker w : workers) 
       security.checkAccess(w.thread); 
     } finally { 
      mainLock.unlock(); 
     } 
    } 
} 

Как вы можете видеть, это безусловно вызывает checkPermission(shutdownPerm) на менеджера безопасности .... shutdownPerm определяется как ... частных статических окончательного RuntimePermission shutdownPerm = новый RuntimePermission ("modifyThread");

... который не имеет абсолютно никакого смысла, насколько я могу сказать, потому что modifyThread подразумевает доступ к системных потоков, и не никаких системных потоков в игре здесь, на самом деле, нет никаких нитей вообще, потому что Я не подавал никаких работ или предварований, и даже если бы были потоки, они были бы моими потоками, потому что я прошел в ThreadFactory. Спецификация ничего не говорит о волшебном умирании, кроме этого, если задействованы потоки системы (их нет), может быть SecurityException.

В принципе, почему я не могу просто удалить строку, которая проверяет доступ к системным потокам? Я не вижу никаких последствий для безопасности, требующих этого. И как никто другой не сталкивался с этой проблемой ??? Я видел сообщение о проблеме, где они «разрешили» эту проблему, изменив звонок на shutdownNow на shutdown, очевидно, что это не исправило их.

+0

«[...] он позволяет вам создавать собственные потоки, что является доказательством того, что не должно быть никаких« системных потоков »[...]« Очевидно, что в ваших рассуждениях есть недостаток. Но да, это можно сделать по-другому. Вот как JSR166 определяет API. –

+0

Хмм ... звучит как-то, что, возможно, придется посетить http://bugs.sun.com/ –

+0

@Tom Hawtin: Ах, я не знал, что JSR определили эту часть SE, спасибо, что упомянули об этом. Однако не API, указанный только javadoc в этом JSR? В этом случае он все еще только говорит: «SecurityException - если существует менеджер безопасности и завершение работы этого ExecutorService может работать с потоками, которые вызывающему не разрешено изменять, поскольку он не поддерживает RuntimePermission (« modifyThread ») или метод checkAccess диспетчера безопасности запрещает доступ ». Я не вижу причин для манипулирования потоками системы в моем случае, а checkAccess (Thread) по умолчанию защищает только системные потоки. –

ответ

1

Это довольно просто: вы не можете сделать это в группе основного потока. Он частично разработан для апплетов. Копирование из метода остановки работы почему? Вы можете свободно использовать PrivilegedAction для выключения вызова, если это проблема. Имейте в виду, что Thread.interrupt() как невинный он может выглядеть также throws SecurityException.

Чтобы ответить на вопрос: просто убедитесь, что вы предоставили свой собственный код для разрешений, и вы счастливы. Альтернативно, «modifyThread» также может быть предоставлен свободно, он используется в основном апплетами.

Что касается ненадежного кода: ну, не доверяемый код даже не должен иметь дело с потоками вне его ThreadGroup, поэтому предоставите API для создания ThreadPool и разрешите выключение для такого, созданного вызывающим. Вы можете предоставить разрешение на основе вызывающего.

Надеется, что это помогло немного (количество знаков вопроса ясно показывает отчаяние и максимальную досаду, хотя)

/* 
    * Conceptually, shutdown is just a matter of changing the 
    * runState to SHUTDOWN, and then interrupting any worker 
    * threads that might be blocked in getTask() to wake them up 
    * so they can exit. Then, if there happen not to be any 
    * threads or tasks, we can directly terminate pool via 
    * tryTerminate. Else, the last worker to leave the building 
    * turns off the lights (in workerDone). 
    * 
    * But this is made more delicate because we must cooperate 
    * with the security manager (if present), which may implement 
    * policies that make more sense for operations on Threads 
    * than they do for ThreadPools. This requires 3 steps: 
    * 
    * 1. Making sure caller has permission to shut down threads 
    * in general (see shutdownPerm). 
    * 
    * 2. If (1) passes, making sure the caller is allowed to 
    * modify each of our threads. This might not be true even if 
    * first check passed, if the SecurityManager treats some 
    * threads specially. If this check passes, then we can try 
    * to set runState. 
    * 
    * 3. If both (1) and (2) pass, dealing with inconsistent 
    * security managers that allow checkAccess but then throw a 
    * SecurityException when interrupt() is invoked. In this 
    * third case, because we have already set runState, we can 
    * only try to back out from the shutdown as cleanly as 
    * possible. Some workers may have been killed but we remain 
    * in non-shutdown state (which may entail tryTerminate from 
    * workerDone starting a new worker to maintain liveness.) 
    */ 
0

Похоже, ленивые и/или безопасной реализацию. Вместо того, чтобы проверять, задействованы ли другие потоки, это просто предполагает, что некоторые из них. Лучше бросить исключение безопасности, а не оставить потенциальную дыру в безопасности.

Смежные вопросы