From d8046fba2dca471a187fc29547519c30a289429c Mon Sep 17 00:00:00 2001 From: "R. Timothy Edwards" Date: Tue, 23 Jun 2026 10:08:31 -0400 Subject: [PATCH] Corrected the other issue with timers in magic, which is that the "progress report" of percent completion on certain long-running processes (namely extraction, DEF reads, and GDS/CIF writes) was allowing Tcl/Tk events to be processed so that the display would be repainted and the console window updated to show the progress instead of hanging. But that was allowing any key macros or commands to be entered and executed, potentially corrupting the database while the process was running. I have used Tk_RestrictEvents() to prevent key and button events from being processed until afterward. This preserves the display updates (which do not alter the database) while preventing commands from being run during one of these long-running processes. Also: Previously, whenever a long-running process printed the status, it would not update at the end, leaving output like "52% complete" being the last output, leaving the impression that the process never finished. Changed the code so that if any partial progress is printed, then it will always finish up with the output "100% complete" so that it is clear to the user that everything went according to plan. --- VERSION | 2 +- cif/CIFhier.c | 21 ++++++++++++++++ extract/ExtSubtree.c | 37 ++++++++++++++++++++++++--- lef/defRead.c | 22 ++++++++++++++++ lef/lefRead.c | 60 -------------------------------------------- tcltk/tclmagic.c | 29 +++++++++++++++++++++ tcltk/tclmagic.h | 3 +++ utils/signals.c | 1 + 8 files changed, 111 insertions(+), 64 deletions(-) diff --git a/VERSION b/VERSION index e06d126b..996636a1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.3.665 +8.3.666 diff --git a/cif/CIFhier.c b/cif/CIFhier.c index d9b36025..8b764140 100644 --- a/cif/CIFhier.c +++ b/cif/CIFhier.c @@ -719,6 +719,20 @@ CIFGenSubcells( SearchContext scx; int cuts, totcuts; float pdone, plast; + bool longTime = FALSE; + +#ifdef MAGIC_WRAPPER + /* This routine may be long-running and events to refresh the + * console will be periodically run to allow the progress status + * to be displayed. Force a restriction on event processing to + * exclude key and button events so that it is not possible to + * corrupt the database during GDS/CIF writes. + */ + Tk_RestrictProc *oldProc; + ClientData oldArg; + + oldProc = Tk_RestrictEvents(RestrictInputProc, (ClientData)NULL, &oldArg); +#endif /* MAGIC_WRAPPER */ UndoDisable(); CIFInitCells(); @@ -847,6 +861,7 @@ CIFGenSubcells( /* Only print something if the 5-second timer has expired */ if (GrDisplayStatus == DISPLAY_BREAK_PENDING) { + longTime = TRUE; TxPrintf("Completed %d%%\n", (int)(pdone + 0.5)); plast = pdone; TxFlushOut(); @@ -865,6 +880,12 @@ CIFGenSubcells( GrDisplayStatus = savedDisplayStatus; SigRemoveTimer(); + if (longTime) TxPrintf("Completed 100%%\n"); + +#ifdef MAGIC_WRAPPER + /* Restore full event access */ + Tk_RestrictEvents(oldProc, oldArg, &oldArg); +#endif /* MAGIC_WRAPPER */ UndoEnable(); } diff --git a/extract/ExtSubtree.c b/extract/ExtSubtree.c index e3dbf2ec..5ae6f5bb 100644 --- a/extract/ExtSubtree.c +++ b/extract/ExtSubtree.c @@ -177,6 +177,20 @@ extSubtree(parentUse, reg, f) float pdone, plast; SearchContext scx; int savedDisplayStatus; + bool longTime = FALSE; + +#ifdef MAGIC_WRAPPER + /* This routine may be long-running and events to refresh the + * console will be periodically run to allow the progress status + * to be displayed. Force a restriction on event processing to + * exclude key and button events so that it is not possible to + * corrupt the database during extraction. + */ + Tk_RestrictProc *oldProc; + ClientData oldArg; + + oldProc = Tk_RestrictEvents(RestrictInputProc, (ClientData)NULL, &oldArg); +#endif /* MAGIC_WRAPPER */ /* Use the display timer to force a 5-second progress check */ savedDisplayStatus = GrDisplayStatus; @@ -299,6 +313,7 @@ extSubtree(parentUse, reg, f) /* Only print something if the 5-second timer has expired */ if (GrDisplayStatus == DISPLAY_BREAK_PENDING) { + longTime = TRUE; TxPrintf("Completed %d%%\n", (int)(pdone + 0.5)); plast = pdone; TxFlushOut(); @@ -309,7 +324,7 @@ extSubtree(parentUse, reg, f) #ifdef MAGIC_WRAPPER /* We need to let Tk paint the console display */ - while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0); + while (Tcl_DoOneEvent(TCL_WINDOW_EVENTS | TCL_DONT_WAIT) != 0); #endif } } @@ -352,11 +367,27 @@ done: /* Output connections and node adjustments */ extOutputConns(&ha.ha_connHash, f); HashKill(&ha.ha_connHash); - GrDisplayStatus = savedDisplayStatus; - SigRemoveTimer(); /* Clear the CU_SUB_EXTRACTED flag from all children instances */ DBCellEnum(def, extClearUseFlags, (ClientData)NULL); + + /* If this cell took more than 5 seconds to extract and a + * progress update was made, then complete the progress + * update with a 100% complete message. + */ + GrDisplayStatus = savedDisplayStatus; + SigRemoveTimer(); + if (longTime) + { + TxPrintf("Completed 100%%\n"); + TxFlushOut(); + } + +#ifdef MAGIC_WRAPPER + /* Restore full event access */ + Tk_RestrictEvents(oldProc, oldArg, &oldArg); +#endif /* MAGIC_WRAPPER */ + } #ifdef exactinteractions diff --git a/lef/defRead.c b/lef/defRead.c index 5d2554fe..46c59b73 100644 --- a/lef/defRead.c +++ b/lef/defRead.c @@ -2498,6 +2498,11 @@ DefRead( NULL }; +#ifdef MAGIC_WRAPPER + Tk_RestrictProc *oldProc; + ClientData oldArg; +#endif /* MAGIC_WRAPPER */ + /* "annotate" implies "dolabels" whether set or not */ if (annotate) dolabels = TRUE; @@ -2519,6 +2524,17 @@ DefRead( return NULL; } +#ifdef MAGIC_WRAPPER + /* This routine may be long-running and events to refresh the + * console will be periodically run to allow the progress status + * to be displayed. Force a restriction on event processing to + * exclude key and button events so that it is not possible to + * corrupt the database during DEF reads. + */ + + oldProc = Tk_RestrictEvents(RestrictInputProc, (ClientData)NULL, &oldArg); +#endif /* MAGIC_WRAPPER */ + /* Initialize */ TxPrintf("Reading DEF data from file %s.\n", filename); @@ -2739,5 +2755,11 @@ DefRead( if (f != NULL) fclose(f); UndoEnable(); + +#ifdef MAGIC_WRAPPER + /* Restore full event access */ + Tk_RestrictEvents(oldProc, oldArg, &oldArg); +#endif /* MAGIC_WRAPPER */ + return rootDef; } diff --git a/lef/lefRead.c b/lef/lefRead.c index 46668d9a..a083ed15 100644 --- a/lef/lefRead.c +++ b/lef/lefRead.c @@ -132,66 +132,6 @@ LefEstimate( } } -/* This is the original version, which doesn't use the system itimer */ -/* and which is not very good at maintaining constant intervals. */ - -#if 0 -void -LefEstimate( - int processed, - int total, - const char *item_name) -{ - static int check_interval, partition; - static struct timeval tv_start; - static float last_time; - struct timeval tv; - struct timezone tz; - float cur_time, time_left; - - if (!total) return; - - if (processed == 0) /* Initialization */ - { - GrDisplayStatus = DISPLAY_IN_PROGRESS; - - check_interval = 100; - gettimeofday(&tv_start, &tz); - last_time = 0.0; - } - - if (processed > check_interval) - { - gettimeofday(&tv, &tz); - cur_time = (float)(tv.tv_sec - tv_start.tv_sec) - + ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6); - time_left = (((float)total / (float)processed) - 1) * cur_time; - - /* not likely to happen, but we don't want a divide-by-0 error */ - if (cur_time == 0.0) cur_time = 1.0e-6; - - partition = (int)((float)processed * (float)PRINT_INTERVAL / cur_time); - - /* partition is never less than 1 nor greater than 5% of the total */ - if (partition == 0) partition = 1; - if (partition > (total / 20)) partition = (total / 20); - - check_interval += partition; - - /* Don't print anything in intervals faster than 1 second */ - if ((cur_time - last_time) < 1.0) return; - last_time = cur_time; - - TxPrintf(" Processed %d of %d %s (%2.1f%%).", processed, total, - item_name, (float)(100 * processed) / (float)total); - TxPrintf(" Est. time remaining: %2.1fs\n", time_left); - TxFlushOut(); - } -} - -#endif /* 0 */ - - /* *------------------------------------------------------------ * diff --git a/tcltk/tclmagic.c b/tcltk/tclmagic.c index 710319ff..72deaeb8 100644 --- a/tcltk/tclmagic.c +++ b/tcltk/tclmagic.c @@ -1515,3 +1515,32 @@ Tclmagic_SafeInit(interp) { return Tclmagic_Init(interp); } + +/* Procedure to set up restricted Tk event processing to allow window + * exposure and updates to occur while deferring key and button press + * events. This allows window updates during potentially long-running + * processes like extraction or GDS writes but prevents commands from + * being executed while the process is still running. + */ + +Tk_RestrictAction +RestrictInputProc( + ClientData clientData, + XEvent *eventPtr) +{ + switch (eventPtr->type) + { + case KeyPress: + case KeyRelease: + case ButtonPress: + case ButtonRelease: + return TK_DEFER_EVENT; + + case MotionNotify: + return TK_DISCARD_EVENT; + + default: + return TK_PROCESS_EVENT; + } +} + diff --git a/tcltk/tclmagic.h b/tcltk/tclmagic.h index 92c587c5..8057026f 100644 --- a/tcltk/tclmagic.h +++ b/tcltk/tclmagic.h @@ -34,5 +34,8 @@ extern const char *Tclmagic_InitStubsVersion; extern void TclmagicRegisterCommands(Tcl_Interp *interp); +extern Tk_RestrictAction RestrictInputProc(ClientData, XEvent *); + + #endif /* MAGIC_WRAPPER */ #endif /* _MAGIC__TCLTK__TCLMAGIC_H */ diff --git a/utils/signals.c b/utils/signals.c index 772c7078..482c4906 100644 --- a/utils/signals.c +++ b/utils/signals.c @@ -724,3 +724,4 @@ sigSetAction(int signo, sigRetVal (*handler)(int)) sigvec(signo, &sv, (struct sigvec *)NULL); #endif } +