| | @@ -167,13 +167,13 @@ |
| 167 | 167 | ** the only Tcl API functions that MUST be called prior to being able to call |
| 168 | 168 | ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete |
| 169 | 169 | ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp |
| 170 | 170 | ** and Tcl_Finalize function types are also required. |
| 171 | 171 | */ |
| 172 | | -typedef void (tcl_FindExecutableProc) (const char * argv0); |
| 172 | +typedef void (tcl_FindExecutableProc) (const char *); |
| 173 | 173 | typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 174 | | -typedef void (tcl_DeleteInterpProc) (Tcl_Interp *interp); |
| 174 | +typedef void (tcl_DeleteInterpProc) (Tcl_Interp *); |
| 175 | 175 | typedef void (tcl_FinalizeProc) (void); |
| 176 | 176 | |
| 177 | 177 | /* |
| 178 | 178 | ** The function types for the "hook" functions to be called before and after a |
| 179 | 179 | ** TH1 command makes a call to evaluate a Tcl script. If the "pre" function |
| | @@ -277,10 +277,25 @@ |
| 277 | 277 | if( major==8 && minor==6 && type==TCL_BETA_RELEASE && patchLevel<3 ){ |
| 278 | 278 | return 0; /* NOTE: Disabled on Tcl 8.6b1/b2, SF bug #3399564. */ |
| 279 | 279 | } |
| 280 | 280 | return 1; /* NOTE: For all other cases, assume good. */ |
| 281 | 281 | } |
| 282 | + |
| 283 | +/* |
| 284 | +** Is the loaded version of Tcl one where TIP #285 (asynchronous script |
| 285 | +** cancellation) is available? This should return non-zero only for Tcl |
| 286 | +** 8.6 and higher. |
| 287 | +*/ |
| 288 | +static int canUseTip285(){ |
| 289 | + int major = -1, minor = -1, patchLevel = -1, type = -1; |
| 290 | + |
| 291 | + Tcl_GetVersion(&major, &minor, &patchLevel, &type); |
| 292 | + if( major<0 || minor<0 || patchLevel<0 || type<0 ){ |
| 293 | + return 0; /* NOTE: Invalid version info, assume bad. */ |
| 294 | + } |
| 295 | + return (major>8 || (major==8 && minor>=6)); |
| 296 | +} |
| 282 | 297 | |
| 283 | 298 | /* |
| 284 | 299 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| 285 | 300 | ** interpreter. Stores the created Tcl interpreter in the Tcl context supplied |
| 286 | 301 | ** by the caller. This must be declared here because quite a few functions in |
| | @@ -379,10 +394,11 @@ |
| 379 | 394 | tcl_CreateInterpProc *xCreateInterp; /* Tcl_CreateInterp() pointer. */ |
| 380 | 395 | tcl_DeleteInterpProc *xDeleteInterp; /* Tcl_DeleteInterp() pointer. */ |
| 381 | 396 | tcl_FinalizeProc *xFinalize; /* Tcl_Finalize() pointer. */ |
| 382 | 397 | Tcl_Interp *interp; /* The on-demand created Tcl interpreter. */ |
| 383 | 398 | int useObjProc; /* Non-zero if an objProc can be called directly. */ |
| 399 | + int useTip285; /* Non-zero if TIP #285 is available. */ |
| 384 | 400 | char *setup; /* The optional Tcl setup script. */ |
| 385 | 401 | tcl_NotifyProc *xPreEval; /* Optional, called before Tcl_Eval*(). */ |
| 386 | 402 | void *pPreContext; /* Optional, provided to xPreEval(). */ |
| 387 | 403 | tcl_NotifyProc *xPostEval; /* Optional, called after Tcl_Eval*(). */ |
| 388 | 404 | void *pPostContext; /* Optional, provided to xPostEval(). */ |
| | @@ -923,22 +939,25 @@ |
| 923 | 939 | int evaluateTclWithEvents( |
| 924 | 940 | Th_Interp *interp, |
| 925 | 941 | void *pContext, |
| 926 | 942 | const char *zScript, |
| 927 | 943 | int nScript, |
| 944 | + int bCancel, |
| 928 | 945 | int bWait, |
| 929 | 946 | int bVerbose |
| 930 | 947 | ){ |
| 931 | 948 | struct TclContext *tclContext = (struct TclContext *)pContext; |
| 932 | 949 | Tcl_Interp *tclInterp; |
| 933 | 950 | int rc; |
| 934 | 951 | int flags = TCL_ALL_EVENTS; |
| 952 | + int useTip285; |
| 935 | 953 | |
| 936 | 954 | if( createTclInterp(interp, pContext)!=TH_OK ){ |
| 937 | 955 | return TH_ERROR; |
| 938 | 956 | } |
| 939 | 957 | tclInterp = tclContext->interp; |
| 958 | + useTip285 = bCancel ? tclContext->useTip285 : 0; |
| 940 | 959 | rc = Tcl_EvalEx(tclInterp, zScript, nScript, TCL_EVAL_GLOBAL); |
| 941 | 960 | if( rc!=TCL_OK ){ |
| 942 | 961 | if( bVerbose ){ |
| 943 | 962 | const char *zResult = getTclResult(tclInterp, 0); |
| 944 | 963 | fossil_print("%s: ", getTclReturnCodeName(rc, 0)); |
| | @@ -945,13 +964,20 @@ |
| 945 | 964 | fossil_print("%s\n", zResult); |
| 946 | 965 | } |
| 947 | 966 | return rc; |
| 948 | 967 | } |
| 949 | 968 | if( !bWait ) flags |= TCL_DONT_WAIT; |
| 969 | + Tcl_Preserve((ClientData)tclInterp); |
| 950 | 970 | while( Tcl_DoOneEvent(flags) ){ |
| 951 | | - /* do nothing */ |
| 971 | + if( Tcl_InterpDeleted(tclInterp) ){ |
| 972 | + break; |
| 973 | + } |
| 974 | + if( useTip285 && Tcl_Canceled(tclInterp, 0)!=TCL_OK ){ |
| 975 | + break; |
| 976 | + } |
| 952 | 977 | } |
| 978 | + Tcl_Release((ClientData)tclInterp); |
| 953 | 979 | return rc; |
| 954 | 980 | } |
| 955 | 981 | |
| 956 | 982 | /* |
| 957 | 983 | ** Creates and initializes a Tcl interpreter for use with the specified TH1 |
| | @@ -1032,14 +1058,19 @@ |
| 1032 | 1058 | Tcl_DeleteInterp(tclInterp); |
| 1033 | 1059 | tclContext->interp = tclInterp = 0; |
| 1034 | 1060 | return TH_ERROR; |
| 1035 | 1061 | } |
| 1036 | 1062 | /* |
| 1037 | | - ** Determine if an objProc can be called directly for a Tcl command invoked |
| 1038 | | - ** via the tclInvoke TH1 command. |
| 1063 | + ** Determine (and cache) if an objProc can be called directly for a Tcl |
| 1064 | + ** command invoked via the tclInvoke TH1 command. |
| 1039 | 1065 | */ |
| 1040 | 1066 | tclContext->useObjProc = canUseObjProc(); |
| 1067 | + /* |
| 1068 | + ** Determine (and cache) whether or not we can use TIP #285 (asynchronous |
| 1069 | + ** script cancellation). |
| 1070 | + */ |
| 1071 | + tclContext->useTip285 = canUseTip285(); |
| 1041 | 1072 | /* Add the TH1 integration commands to Tcl. */ |
| 1042 | 1073 | Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); |
| 1043 | 1074 | Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); |
| 1044 | 1075 | Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); |
| 1045 | 1076 | /* If necessary, evaluate the custom Tcl setup script. */ |
| 1046 | 1077 | |