Поддерживают ли какие-либо языки «истинную» модульность в процессе? - PullRequest
2 голосов
/ 01 марта 2012

Я читал Principles of Computer System Design (Saltzer & Kaashoek), и одна из первых глав посвящена модульности. Например:

  • Различные модули должны взаимодействовать только через указанные интерфейсы
  • Модули должны быть сконструированы таким образом, чтобы предоставлять как можно меньше их внутренней реализации. возможно

Теперь это все довольно стандартные вещи, и именно так работают большинство языков OO. Тем не менее, они упоминают даже более строгие требования, такие как:

  • Вызываемый модуль не должен быть в состоянии заблокировать вызывающего абонента, не вернув
  • Вызываемый модуль не должен вызывать смерть вызывающей программы из-за нехватки места в стеке

Теперь эти требования имеют смысл для меня и сделают многое, чтобы остановить ошибки, распространяющиеся по всей массивной программе, и привести к их краху.

Однако метод, который они предлагают, разделяя все на клиент-серверные процессы, во многих целях кажется излишним. Написание всего , которое вы хотите модулировать, так как клиент / сервер выглядит так:

  • невероятно утомительно
  • медленно (со скоростью выполнения)

Например, я хотел бы иметь возможность делегировать работу моему модулю Math и накладывать ограничения на время выполнения и использование памяти, но на самом деле я не хочу создавать отдельный сервер Math * 1031. * работает в фоновом режиме только для этих преимуществ! Кроме того, непроизводительные издержки при передаче сообщений IPC (как при вычислениях, так и при задержке), несомненно, будут довольно значительными по сравнению с прямыми вызовами процедур.

Существуют ли языки, обеспечивающие модульность такого типа в рамках процесса? Как промежуточное звено перед жесткой связью прямых процедурных вызовов и издержек многопроцессного проектирования клиент-сервер? То, как я это себе представляю, вместо:

y = Math.sin(x)

, что оставляет меня открытым для бесконечных циклов и переполнений стека в модуле Math, я хотел бы сделать что-то вроде

y = try(maxMemory=1024kb, maxTime=12ms){
    Math.sin(x)
}catch(Anything){
    0
}

Что бы установить значение y по умолчанию (0), если что-то не получится в пределах Math.sin. Неверный ввод, ошибки, бесконечные циклы, исключения времени выполнения, что угодно. Существует ли что-то подобное уже на каком-то языке, и если нет, то почему? Мне кажется, что это было бы очень полезно в самых разных местах.

1 Ответ

1 голос
/ 01 марта 2012

Вы можете сделать это в Racket , используя библиотеку sandbox :

> (require racket/sandbox)
> (define x 0) ;; some input value
> (call-with-limits 0.012 ;; 12 ms
                    1     ;; 1 MB
                    (lambda () (sin x)))
0

> (call-with-limits 1 ;; 1 sec
                    1 ;; 1 MB
                    (lambda () (sleep 2) 'done))
with-limit: out of time

> (call-with-limits #f ;; no time limit
                    1  ;; 1 MB
                    (lambda () 
                      (let loop () 
                        (cons (make-vector 10000) (loop)))))
with-limit: out of memory

Код запускается в отдельном упрощенном ("зеленом") потоке Racket (не в отдельном процессе ОС).

В последних двух случаях возникают исключения, которые вы можете перехватить, если хотите заменить альтернативное значение.

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

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

...