如何中断Tcl_eval

如何中断Tcl_eval,tcl,Tcl,我有一个小型shell应用程序,它嵌入了Tcl 8.4来执行一些Tcl代码集。使用Tcl\u CreateInterp初始化Tcl解释器。一切都很简单: 用户类型Tcl命令 命令被传递到Tcl\u Eval进行评估 重复 问:有没有办法中断很长的Tcl_Eval命令?我可以处理“Ctrl+C”信号,但如何中断Tcl_Eval?Tcl默认情况下不设置信号处理程序(SIGPIPE除外,您可能根本不关心它),因此需要使用该语言的扩展来获得所需的功能。 到目前为止,最简单的方法是使用来自(或来自Expe

我有一个小型shell应用程序,它嵌入了Tcl 8.4来执行一些Tcl代码集。使用
Tcl\u CreateInterp
初始化Tcl解释器。一切都很简单:

  • 用户类型Tcl命令
  • 命令被传递到
    Tcl\u Eval
    进行评估
  • 重复

  • 问:有没有办法中断很长的
    Tcl_Eval
    命令?我可以处理“Ctrl+C”信号,但如何中断
    Tcl_Eval

    Tcl默认情况下不设置信号处理程序(SIGPIPE除外,您可能根本不关心它),因此需要使用该语言的扩展来获得所需的功能。 到目前为止,最简单的方法是使用来自(或来自Expect包的)的
    signal
    命令,但这在其他方面更具侵入性:

    在使用
    Tcl\u Eval()
    开始运行您希望能够中断的代码之前,只需评估包含在同一解释器中的脚本;Ctrl+C将导致
    Tcl\u Eval()
    返回
    Tcl\u ERROR
    。(有一些,比如运行任意的Tcl命令,它可以重新捕获到您的C代码中,但这是最简单的。)

    如果你在Windows上,很明显


    下面是一个互动会话中的实际演示

    bash$ tclsh8.6 % package require Tclx 8.4 % signal error SIGINT % puts [list [catch { while 1 {incr i} } a b] $a $b $errorInfo $errorCode] ^C1 {can't read "i": no such variableSIGINT signal received} {-code 1 -level 0 -errorstack {INNER push1} -errorcode {POSIX SIG SIGINT} -errorinfo {can't read "i": no such variableSIGINT signal received while executing "incr i"} -errorline 2} {can't read "i": no such variableSIGINT signal received while executing "incr i"} {POSIX SIG SIGINT} %
    解决此问题的另一种方法是将您的tcl解释器转移到一个单独的进程中,并从主进程驱动tcl解释器的stdin和stdout。然后,在主进程中,您可以截取Ctrl-C并使用它终止分叉tcl解释器的进程并重新标记新的tcl解释器

    使用此解决方案,tcl解释器将永远不会锁定主程序。但是,如果需要在主进程中运行c函数扩展,那么添加c函数扩展确实很烦人,因为您需要使用进程间通信来调用函数

    我试图解决一个类似的问题,在一个工作线程中启动TCL解释。除此之外,真的没有干净的方法杀死工作线程,因为它使分配的内存处于未清理状态,从而导致内存泄漏。因此,真正解决这个问题的唯一方法是使用流程模型,或者继续退出并重新启动应用程序。考虑到进程解决方案所需的时间,我决定坚持使用线程,并在这几天内解决问题,以使ctrl-c在单独的进程中工作,而不是每次杀死线程时都泄漏内存。还有可能破坏我的程序的稳定和崩溃

    更新:

    我的结论是Tcl数组不是正常变量,在Eval之后不能使用Tcl_GetVar2Ex读取“tmp”变量,并且tmp不会显示在“info globals”下。所以为了解决这个问题,我决定直接调用Tcl库API而不是Eval快捷方式来构建一个dictionary对象来返回

    Tcl_Obj* dict_obj = Tcl_NewDictObj ();
    if (!dict_obj) {
       return TCL_ERROR;
    }
    
    Tcl_DictObjPut (
        interp, 
        dict_obj,
        Tcl_NewStringObj ("key1",     -1),
        Tcl_NewStringObj ("value1",   -1)
    );
    
    Tcl_DictObjPut (
        interp, 
        dict_obj,
        Tcl_NewStringObj ("key2",     -1),
        Tcl_NewStringObj ("value2",   -1)
    );
    
    Tcl_SetObjResult(interp, dict_obj);
    

    谢谢你的解决方案。问题是我从C代码运行
    Tcl\u Eval
    。使用常规的
    signal
    功能,在此C代码中捕获并处理所有信号。因此,如果可能的话,最好用C语言来实现。包括一个额外的软件包也会带来后勤安装方面的挑战。@ilya1725因此,请查看
    信号的实现方式并复制它。它是BSD授权的。(一些比特需要从其他地方复制,但大部分是相当孤立的。)
    
    % try {
        while 1 {incr i}
    } trap {POSIX SIG SIGINT} -> {
        puts "interrupt"
    }
    ^Cinterrupt
    % 
    
    Tcl_Obj* dict_obj = Tcl_NewDictObj ();
    if (!dict_obj) {
       return TCL_ERROR;
    }
    
    Tcl_DictObjPut (
        interp, 
        dict_obj,
        Tcl_NewStringObj ("key1",     -1),
        Tcl_NewStringObj ("value1",   -1)
    );
    
    Tcl_DictObjPut (
        interp, 
        dict_obj,
        Tcl_NewStringObj ("key2",     -1),
        Tcl_NewStringObj ("value2",   -1)
    );
    
    Tcl_SetObjResult(interp, dict_obj);