在 lisp 中执行期间的函数重定义

Function redefinitions during execution in lisp

假设我们有两个函数 fct1fct2 :

让我们假设以下约束必须始终为真:

如果 fct1 的通话期间会发生什么:

是否可以通过将 01 设置为状态 A 并将 02 设置为状态 not(B) 来 "breaks" 约束?

我找到了这个答案:

If a recursive function redefines itself, the recursive calls still to be made in the same invocation may keep going to the same body. [...] More generally, Common Lisp allows compilers to generate efficient calls among functions that are in the same file. So you normally have to think of replacement of running code as being at the module level rather than individual function level. If functions A and B are in the same module, and A calls B, then if you merely replace B without replacing A, A may continue calling the old B (because B was inlined into A, or because A doesn't go through the symbol, but uses a more direct address for B). You can declare functions notinline to suppress this.

我的问题是:

如果"yes":

这是一个示例,说明您所描述的情况在多线程环境中是如何发生的:

(progn

  (defun f2 (o2)
    (setf (car o2) :b))

  (defun f1 (o1 o2)
    (setf (car o1) :a)
    ;; the sleep here is to increase the risk of data-race
    (sleep 3)
    (f2 o2))

  ;; call the functions in a separate thread
  (sb-thread:make-thread
   (lambda () 
     (let ((o1 (list 0))
           (o2 (list 0)))
       (f1 o1 o2)
       (print (list o1 o2)))))

  ;; in parallel, redefine f2, then f1
  (defun f2 (o2)
    (setf (car o2) :not-b))

  (defun f1 (o1 o2)
    (setf (car o1) :not-a)
    (f2 o2)))

3 秒后,REPL 打印

((:A) (:NOT-B))

如果在定义f2之前加上(declaim (inline f2))再测试,那么oldf2的代码还是从old f1,在线程内部,3 秒后打印以下内容:

((:A) (:B)) 

进一步调用更新函数 f1 得到:

((:NOT-A) (:NOT-B)) 

What tool(s) can I use to test if the functions work the correct way ? It seems a pain to test : I have no idea how to test redefinitions without altering the base source code.

也许您正在使用新代码更新 运行 服务器,并且您希望避免服务器在您加载定义时使用函数的部分重新定义。

与基础架构的所有其他方面一样,重要的是提前计划如何可靠地进行备份和更新(数据库、配置等)。

一种可能的方法是逐包更新。你可以在你的包后加上版本号:

(in-package :web-0 ...)
(defun f2 () ...)
(defun f1 () ...)

;; new version
(in-package :web-1 ...)
(defun f2 () ...)
(defun f1 () ...)

当需要更新时,您可以编译和加载 :web-1 的代码,而不会干扰 运行 的代码。那么您应该能够更改调用者以使用新的实现:

;; somewhere in the main server package
(handle-request (request)
   (web-0:handle request))

 ;; update it
(handle-request (request)
  (web-1:handle request))

它甚至可以在没有版本号的情况下工作,如果你先删除包然后用相同的名称重新创建它,但是你不能轻易恢复。

有些地方可能还需要全局锁,您必须在应用程序中和更新期间管理它。