summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2024-09-17 14:13:14 (GMT)
committersebres <sebres@users.sourceforge.net>2024-09-17 14:13:14 (GMT)
commit18ed5377e67970c87daf0c2d6f0fee40e4744582 (patch)
treebbdc161b100a086dd602eaf2b6b08bfe48a3e2ca
parente5dcd8f739b96df18f98713d33b39c963c435bbd (diff)
parentf2b14cb7f00034cf7c9ee3e5681e4c912ca7ff9c (diff)
downloadtcl-18ed5377e67970c87daf0c2d6f0fee40e4744582.zip
tcl-18ed5377e67970c87daf0c2d6f0fee40e4744582.tar.gz
tcl-18ed5377e67970c87daf0c2d6f0fee40e4744582.tar.bz2
merge bug-02d5d65d70adab97: avoid unneeded (but expensive) path normalization for several file subsystem commands and operations;
closes [02d5d65d70adab97]
-rw-r--r--generic/tclIOUtil.c14
-rw-r--r--tests-perf/file.perf.tcl77
-rw-r--r--unix/tclUnixChan.c19
-rw-r--r--unix/tclUnixFile.c4
-rw-r--r--win/tclWinChan.c21
-rw-r--r--win/tclWinFile.c4
6 files changed, 126 insertions, 13 deletions
diff --git a/generic/tclIOUtil.c b/generic/tclIOUtil.c
index e630702..847d191 100644
--- a/generic/tclIOUtil.c
+++ b/generic/tclIOUtil.c
@@ -469,6 +469,11 @@ TclFSCwdIsNative(void)
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&fsDataKey);
+ /* if not yet initialized - ensure we'll once obtain cwd */
+ if (!tsdPtr->cwdPathEpoch) {
+ Tcl_FSGetCwd(NULL);
+ }
+
if (tsdPtr->cwdClientData != NULL) {
return 1;
} else {
@@ -2230,15 +2235,6 @@ Tcl_FSOpenFileChannel(
const Tcl_Filesystem *fsPtr;
Tcl_Channel retVal = NULL;
- /*
- * We need this just to ensure we return the correct error messages under
- * some circumstances.
- */
-
- if (Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL) {
- return NULL;
- }
-
fsPtr = Tcl_FSGetFileSystemForPath(pathPtr);
if (fsPtr != NULL && fsPtr->openFileChannelProc != NULL) {
int mode, seekFlag, binary;
diff --git a/tests-perf/file.perf.tcl b/tests-perf/file.perf.tcl
new file mode 100644
index 0000000..53dd4cc
--- /dev/null
+++ b/tests-perf/file.perf.tcl
@@ -0,0 +1,77 @@
+#!/usr/bin/tclsh
+
+# ------------------------------------------------------------------------
+#
+# file.perf.tcl --
+#
+# This file provides performance tests for comparison of tcl-speed
+# of file commands and subsystem.
+#
+# ------------------------------------------------------------------------
+#
+# Copyright (c) 2024 Serg G. Brester (aka sebres)
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file.
+#
+
+
+if {![namespace exists ::tclTestPerf]} {
+ source -encoding utf-8 [file join [file dirname [info script]] test-performance.tcl]
+}
+
+
+namespace eval ::tclTestPerf-File {
+
+namespace path {::tclTestPerf}
+
+proc _get_new_file_path_obj [list [list p [info script]]] {
+ # always generate new string object here (ensure it is not a "cached" object of type path):
+ string trimright "$p "; # costs of object "creation" smaller than 1 microsecond
+}
+
+# regression tests for bug-02d5d65d70adab97 (fix for [02d5d65d70adab97]):
+proc test-file-access-regress {{reptime 1000}} {
+ _test_run -no-result $reptime {
+ setup { set fn [::tclTestPerf-File::_get_new_file_path_obj] }
+ # file exists on "cached" file path:
+ { file exists $fn }
+ # file exists on not "cached" (fresh generated) file path:
+ { set fn [::tclTestPerf-File::_get_new_file_path_obj]; file exists $fn }
+
+ setup { set fn [::tclTestPerf-File::_get_new_file_path_obj] }
+ # file attributes on "cached" file path:
+ { file attributes $fn -readonly }
+ # file attributes on not "cached" (fresh generated) file path:
+ { set fn [::tclTestPerf-File::_get_new_file_path_obj]; file attributes $fn -readonly }
+
+ setup { set fn [::tclTestPerf-File::_get_new_file_path_obj] }
+ # file stat on "cached" file path:
+ { file stat $fn st }
+ # file stat on not "cached" (fresh generated) file path:
+ { set fn [::tclTestPerf-File::_get_new_file_path_obj]; file stat $fn st }
+
+ setup { set fn [::tclTestPerf-File::_get_new_file_path_obj] }
+ # touch on "cached" file path:
+ { close [open $fn rb] }
+ # touch on not "cached" (fresh generated) file path:
+ { set fn [::tclTestPerf-File::_get_new_file_path_obj]; close [open $fn rb] }
+ }
+}
+
+proc test {{reptime 1000}} {
+ test-file-access-regress $reptime
+
+ puts \n**OK**
+}
+
+}; # end of ::tclTestPerf-File
+
+# ------------------------------------------------------------------------
+
+# if calling direct:
+if {[info exists ::argv0] && [file tail $::argv0] eq [file tail [info script]]} {
+ array set in {-time 500}
+ array set in $argv
+ ::tclTestPerf-File::test $in(-time)
+}
diff --git a/unix/tclUnixChan.c b/unix/tclUnixChan.c
index 1844a23..e072cd7 100644
--- a/unix/tclUnixChan.c
+++ b/unix/tclUnixChan.c
@@ -12,6 +12,7 @@
*/
#include "tclInt.h" /* Internal definitions for Tcl. */
+#include "tclFileSystem.h"
#include "tclIO.h" /* To get Channel type declaration. */
#undef SUPPORTS_TTY
@@ -1418,6 +1419,24 @@ TclpOpenFileChannel(
native = (const char *)Tcl_FSGetNativePath(pathPtr);
if (native == NULL) {
if (interp != (Tcl_Interp *) NULL) {
+ /*
+ * We need this just to ensure we return the correct error messages under
+ * some circumstances (relative paths only), so because the normalization
+ * is very expensive, don't invoke it for native or absolute paths.
+ */
+ if (
+ (
+ (
+ !TclFSCwdIsNative() &&
+ (Tcl_FSGetPathType(pathPtr) != TCL_PATH_ABSOLUTE)
+ ) ||
+ (*TclGetString(pathPtr) == '~') /* possible tilde expansion */
+ ) &&
+ Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL
+ ) {
+ return NULL;
+ }
+
Tcl_AppendResult(interp, "couldn't open \"",
TclGetString(pathPtr), "\": filename is invalid on this platform",
(char *)NULL);
diff --git a/unix/tclUnixFile.c b/unix/tclUnixFile.c
index c39e7b6..9769b13 100644
--- a/unix/tclUnixFile.c
+++ b/unix/tclUnixFile.c
@@ -1076,9 +1076,9 @@ TclNativeCreateNativeRep(
Tcl_Obj *validPathPtr;
int len;
- if (TclFSCwdIsNative()) {
+ if (TclFSCwdIsNative() || Tcl_FSGetPathType(pathPtr) == TCL_PATH_ABSOLUTE) {
/*
- * The cwd is native, which means we can use the translated path
+ * The cwd is native (or path is absolute), use the translated path
* without worrying about normalization (this will also usually be
* shorter so the utf-to-external conversion will be somewhat faster).
*/
diff --git a/win/tclWinChan.c b/win/tclWinChan.c
index 666a0b1..0199a37 100644
--- a/win/tclWinChan.c
+++ b/win/tclWinChan.c
@@ -11,6 +11,7 @@
*/
#include "tclWinInt.h"
+#include "tclFileSystem.h"
#include "tclIO.h"
/*
@@ -866,6 +867,26 @@ TclpOpenFileChannel(
nativeName = (const WCHAR *)Tcl_FSGetNativePath(pathPtr);
if (nativeName == NULL) {
if (interp != (Tcl_Interp *) NULL) {
+ /*
+ * We need this just to ensure we return the correct error messages under
+ * some circumstances (relative paths only), so because the normalization
+ * is very expensive, don't invoke it for native or absolute paths.
+ * Note: TODO - check one needs consider tilde expansion after TIP#602,
+ * (proper error message of tests *io-40.17 "tilde substitution in open")
+ */
+ if (
+ (
+ (
+ !TclFSCwdIsNative() &&
+ (Tcl_FSGetPathType(pathPtr) != TCL_PATH_ABSOLUTE)
+ ) ||
+ (*TclGetString(pathPtr) == '~') /* possible tilde expansion */
+ ) &&
+ Tcl_FSGetNormalizedPath(interp, pathPtr) == NULL
+ ) {
+ return NULL;
+ }
+
Tcl_AppendResult(interp, "couldn't open \"",
TclGetString(pathPtr), "\": filename is invalid on this platform",
NULL);
diff --git a/win/tclWinFile.c b/win/tclWinFile.c
index 633e2ce..b8e2e30 100644
--- a/win/tclWinFile.c
+++ b/win/tclWinFile.c
@@ -3036,9 +3036,9 @@ TclNativeCreateNativeRep(
Tcl_DString ds;
Tcl_Encoding utf8;
- if (TclFSCwdIsNative()) {
+ if (TclFSCwdIsNative() || Tcl_FSGetPathType(pathPtr) == TCL_PATH_ABSOLUTE) {
/*
- * The cwd is native, which means we can use the translated path
+ * The cwd is native (or path is absolute), use the translated path
* without worrying about normalization (this will also usually be
* shorter so the utf-to-external conversion will be somewhat faster).
*/