如何中断Tcl_eval

时间:2014-01-10 00:17:26

标签: tcl

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

  1. 用户类型Tcl命令
  2. 命令传递给Tcl_Eval进行评估
  3. 重复
  4. 问:有没有办法打断很长的Tcl_Eval命令?我可以处理'Ctrl + C'信号,但是如何中断Tcl_Eval

2 个答案:

答案 0 :(得分:1)

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

package require Tclx

# Make Ctrl+C generate an error
signal error SIGINT

在使用Tcl_Eval()开始运行您希望能够中断的代码之前,只需评估包含同一解释器中的脚本的脚本; a Ctrl + C 会导致Tcl_Eval()返回TCL_ERROR。 (有other things you can do - 例如运行任意Tcl命令,它可以回收到你的C代码 - 但这是最​​简单的。)

如果你在Windows上,显然是TWAPI package can do something equivalent


以下是交互式会话中的演示!

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}
% 

另请注意,这可能会使解释器处于奇怪的状态;错误信息有点奇怪(事实上,这将是一个错误,但我不知道是什么)。像这样(在8.6中)这样做可能更优雅:

% try {
    while 1 {incr i}
} trap {POSIX SIG SIGINT} -> {
    puts "interrupt"
}
^Cinterrupt
% 

答案 1 :(得分:0)

解决这个问题的另一种方法是将你的tcl解释器分成一个单独的进程,并从你的主进程驱动tcl解释器的stdin和stdout。然后,在主进程中,您可以拦截Ctrl-C并使用它来终止分叉tcl解释器的进程并重新生成新的tcl解释器。

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

我有一个类似的问题,我试图解决,我在工作线程中启动TCL解释。除此之外,实际上没有杀死工作线程的干净方法,因为它将分配的内存保留在未清除的状态,导致内存泄漏。因此,解决此问题的唯一方法是使用流程模型,或者只是继续退出并重新启动应用程序。考虑到使用流程解决方案所需的时间,我决定坚持使用线程并解决问题,以便让ctrl-c在单独的进程中工作,而不是每次杀死线程时泄漏内存。和潜在的破坏稳定和崩溃我的计划。

更新:

我的结论是Tcl Arrays不是正常变量,你不能使用Tcl_GetVar2Ex在Eval之后读取“tmp”变量,并且tmp不会出现在“info globals”之下。因此,为了解决这个问题,我决定直接调用Tcl-Library API而不是Eval快捷方式来构建要返回的字典对象。

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);