{"thread":{"id":"38698","subject":"[PATCH v5 1/3] gitk: write only changed configuration variables","startedAt":"2015-03-04T03:58:15Z","lastAt":"2015-03-22T03:39:15Z","messageCount":5,"participants":["Max Kirillov","Paul Mackerras"],"isPatch":true,"patchVersion":5,"patchTotal":3},"messages":[{"id":"256957","messageId":"1425441498-29416-1-git-send-email-max@max630.net","threadId":"38698","inReplyTo":null,"subject":"[PATCH v5 0/3] gitk: save only changed configuration on exit","fromName":"Max Kirillov","fromEmail":"max@max630.net","sentAt":"2015-03-04T03:58:15Z","receivedAt":"2015-03-04T03:58:15Z","isPatch":true,"sender":{"key":"max@max630.net","avatar":"https://avatars.githubusercontent.com/u/381560?v=4"},"body":"The changes:\n\n* remove unused views_modified_names assignment\n* use if {[catch...] to check saving error\n* split error reporting from busy wait\n\nThe busy wait parameters are unchanged, mostly because I did not have time yet to test them.\n\nMax Kirillov (3):\n  gitk: write only changed configuration variables\n  gitk: report file saving error\n  gitk: synchronize config write\n\n gitk | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------\n 1 file changed, 105 insertions(+), 14 deletions(-)\n\n-- \n2.1.1.391.g7a54a76\n"},{"id":"256956","messageId":"1425441498-29416-2-git-send-email-max@max630.net","threadId":"38698","inReplyTo":"1425441498-29416-1-git-send-email-max@max630.net","subject":"[PATCH v5 1/3] gitk: write only changed configuration variables","fromName":"Max Kirillov","fromEmail":"max@max630.net","sentAt":"2015-03-04T03:58:16Z","receivedAt":"2015-03-04T03:58:16Z","isPatch":true,"sender":{"key":"max@max630.net","avatar":"https://avatars.githubusercontent.com/u/381560?v=4"},"body":"When gitk contains some changed parameter, and there is existing\ninstance of gitk where the parameter is still old, it is reverted to\nthat old value when the instance exits.\n\nInstead, store a parameter in config only it is has been modified in the\nexiting instance. Otherwise, preserve the value which currently is in\nfile.  This allows editing the configuration when several instances are\nrunning, and don't get rollback of the modification if some other\ninstance where the configuration was not edited is closed last.\n\nFor scalar variables, use trace(3tcl) to detect their change. Since `trace` can\nsend bogus events, doublecheck if the value has really been changed, but once\nit is marked as changed, do not reset it back to unchanged ever, because if\nuser has restored the original value, it's the decision which should be stored\nas well as modified value.\n\nTreat view list especially: instead of rewriting the whole list, merge\nindividual views. Place old and updated views at their older placed, add\nnew ones to the end of list. Collect modified view explicitly, in newviewok{}\nand delview{}.\n\nDo not merge geometry values. They are almost always changing because\nuser moves and resises windows, and there is no way to find which one of\nthe geometries is most desired. Just overwrite them unconditionally,\nlike earlier.\n\nSigned-off-by: Max Kirillov <max@max630.net>\n---\n gitk | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------\n 1 file changed, 76 insertions(+), 10 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 78358a7..5f09756 100755\n--- a/gitk\n+++ b/gitk\n@@ -2776,12 +2776,38 @@ proc doprogupdate {} {\n     }\n }\n \n+proc config_init_trace {name} {\n+    global config_variable_changed config_variable_original\n+\n+    upvar #0 $name var\n+    set config_variable_changed($name) 0\n+    set config_variable_original($name) $var\n+}\n+\n+proc config_variable_change_cb {name name2 op} {\n+    global config_variable_changed config_variable_original\n+\n+    upvar #0 $name var\n+    if {$op eq \"write\" &&\n+\t(![info exists config_variable_original($name)] ||\n+\t $config_variable_original($name) ne $var)} {\n+\tset config_variable_changed($name) 1\n+    }\n+}\n+\n proc savestuff {w} {\n-    global viewname viewfiles viewargs viewargscmd viewperm nextviewnum\n-    global use_ttk\n     global stuffsaved\n     global config_file config_file_tmp\n-    global config_variables\n+    global config_variables config_variable_changed\n+    global viewchanged\n+\n+    upvar #0 viewname current_viewname\n+    upvar #0 viewfiles current_viewfiles\n+    upvar #0 viewargs current_viewargs\n+    upvar #0 viewargscmd current_viewargscmd\n+    upvar #0 viewperm current_viewperm\n+    upvar #0 nextviewnum current_nextviewnum\n+    upvar #0 use_ttk current_use_ttk\n \n     if {$stuffsaved} return\n     if {![winfo viewable .]} return\n@@ -2793,16 +2819,24 @@ proc savestuff {w} {\n \tif {$::tcl_platform(platform) eq {windows}} {\n \t    file attributes $config_file_tmp -hidden true\n \t}\n+\tif {[file exists $config_file]} {\n+\t    source $config_file\n+\t}\n \tforeach var_name $config_variables {\n \t    upvar #0 $var_name var\n-\t    puts $f [list set $var_name $var]\n+\t    upvar 0 $var_name old_var\n+\t    if {!$config_variable_changed($var_name) && [info exists old_var]} {\n+\t\tputs $f [list set $var_name $old_var]\n+\t    } else {\n+\t\tputs $f [list set $var_name $var]\n+\t    }\n \t}\n \n \tputs $f \"set geometry(main) [wm geometry .]\"\n \tputs $f \"set geometry(state) [wm state .]\"\n \tputs $f \"set geometry(topwidth) [winfo width .tf]\"\n \tputs $f \"set geometry(topheight) [winfo height .tf]\"\n-\tif {$use_ttk} {\n+\tif {$current_use_ttk} {\n \t    puts $f \"set geometry(pwsash0) \\\"[.tf.histframe.pwclist sashpos 0] 1\\\"\"\n \t    puts $f \"set geometry(pwsash1) \\\"[.tf.histframe.pwclist sashpos 1] 1\\\"\"\n \t} else {\n@@ -2812,11 +2846,33 @@ proc savestuff {w} {\n \tputs $f \"set geometry(botwidth) [winfo width .bleft]\"\n \tputs $f \"set geometry(botheight) [winfo height .bleft]\"\n \n+\tarray set view_save {}\n+\tarray set views {}\n+\tif {![info exists permviews]} { set permviews {} }\n+\tforeach view $permviews {\n+\t    set view_save([lindex $view 0]) 1\n+\t    set views([lindex $view 0]) $view\n+\t}\n \tputs -nonewline $f \"set permviews {\"\n-\tfor {set v 0} {$v < $nextviewnum} {incr v} {\n-\t    if {$viewperm($v)} {\n-\t\tputs $f \"{[list $viewname($v) $viewfiles($v) $viewargs($v) $viewargscmd($v)]}\"\n+\tfor {set v 1} {$v < $current_nextviewnum} {incr v} {\n+\t    if {$viewchanged($v)} {\n+\t\tif {$current_viewperm($v)} {\n+\t\t    set views($current_viewname($v)) [list $current_viewname($v) $current_viewfiles($v) $current_viewargs($v) $current_viewargscmd($v)]\n+\t\t} else {\n+\t\t    set view_save($current_viewname($v)) 0\n+\t\t}\n+\t    }\n+\t}\n+\t# write old and updated view to their places and append remaining to the end\n+\tforeach view $permviews {\n+\t    set view_name [lindex $view 0]\n+\t    if {$view_save($view_name)} {\n+\t\tputs $f \"{$views($view_name)}\"\n \t    }\n+\t    unset views($view_name)\n+\t}\n+\tforeach view_name [array names views] {\n+\t    puts $f \"{$views($view_name)}\"\n \t}\n \tputs $f \"}\"\n \tclose $f\n@@ -4238,7 +4294,7 @@ proc allviewmenus {n op args} {\n \n proc newviewok {top n {apply 0}} {\n     global nextviewnum newviewperm newviewname newishighlight\n-    global viewname viewfiles viewperm selectedview curview\n+    global viewname viewfiles viewperm viewchanged selectedview curview\n     global viewargs viewargscmd newviewopts viewhlmenu\n \n     if {[catch {\n@@ -4259,6 +4315,7 @@ proc newviewok {top n {apply 0}} {\n \tincr nextviewnum\n \tset viewname($n) $newviewname($n)\n \tset viewperm($n) $newviewopts($n,perm)\n+\tset viewchanged($n) 1\n \tset viewfiles($n) $files\n \tset viewargs($n) $newargs\n \tset viewargscmd($n) $newviewopts($n,cmd)\n@@ -4271,6 +4328,7 @@ proc newviewok {top n {apply 0}} {\n     } else {\n \t# editing an existing view\n \tset viewperm($n) $newviewopts($n,perm)\n+\tset viewchanged($n) 1\n \tif {$newviewname($n) ne $viewname($n)} {\n \t    set viewname($n) $newviewname($n)\n \t    doviewmenu .bar.view 5 [list showview $n] \\\n@@ -4293,7 +4351,7 @@ proc newviewok {top n {apply 0}} {\n }\n \n proc delview {} {\n-    global curview viewperm hlview selectedhlview\n+    global curview viewperm hlview selectedhlview viewchanged\n \n     if {$curview == 0} return\n     if {[info exists hlview] && $hlview == $curview} {\n@@ -4302,6 +4360,7 @@ proc delview {} {\n     }\n     allviewmenus $curview delete\n     set viewperm($curview) 0\n+    set viewchanged($curview) 1\n     showview 0\n }\n \n@@ -12122,6 +12181,10 @@ set config_variables {\n     linehoveroutlinecolor mainheadcirclecolor workingfilescirclecolor\n     indexcirclecolor circlecolors linkfgcolor circleoutlinecolor\n }\n+foreach var $config_variables {\n+    config_init_trace $var\n+    trace add variable $var write config_variable_change_cb\n+}\n \n parsefont mainfont $mainfont\n eval font create mainfont [fontflags mainfont]\n@@ -12249,6 +12312,7 @@ set highlight_related [mc \"None\"]\n set highlight_files {}\n set viewfiles(0) {}\n set viewperm(0) 0\n+set viewchanged(0) 0\n set viewargs(0) {}\n set viewargscmd(0) {}\n \n@@ -12307,6 +12371,7 @@ if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {\n     set viewargs(1) $revtreeargs\n     set viewargscmd(1) $revtreeargscmd\n     set viewperm(1) 0\n+    set viewchanged(1) 0\n     set vdatemode(1) 0\n     addviewmenu 1\n     .bar.view entryconf [mca \"Edit view...\"] -state normal\n@@ -12322,6 +12387,7 @@ if {[info exists permviews]} {\n \tset viewargs($n) [lindex $v 2]\n \tset viewargscmd($n) [lindex $v 3]\n \tset viewperm($n) 1\n+\tset viewchanged($n) 0\n \taddviewmenu $n\n     }\n }\n-- \n2.1.1.391.g7a54a76\n"},{"id":"256959","messageId":"1425441498-29416-3-git-send-email-max@max630.net","threadId":"38698","inReplyTo":"1425441498-29416-1-git-send-email-max@max630.net","subject":"[PATCH v5 2/3] gitk: report file saving error","fromName":"Max Kirillov","fromEmail":"max@max630.net","sentAt":"2015-03-04T03:58:17Z","receivedAt":"2015-03-04T03:58:17Z","isPatch":true,"sender":{"key":"max@max630.net","avatar":"https://avatars.githubusercontent.com/u/381560?v=4"},"body":"Signed-off-by: Max Kirillov <max@max630.net>\n---\n gitk | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/gitk b/gitk\nindex 5f09756..9404d5d 100755\n--- a/gitk\n+++ b/gitk\n@@ -2811,7 +2811,7 @@ proc savestuff {w} {\n \n     if {$stuffsaved} return\n     if {![winfo viewable .]} return\n-    catch {\n+    if {[catch {\n \tif {[file exists $config_file_tmp]} {\n \t    file delete -force $config_file_tmp\n \t}\n@@ -2877,6 +2877,8 @@ proc savestuff {w} {\n \tputs $f \"}\"\n \tclose $f\n \tfile rename -force $config_file_tmp $config_file\n+    } err]} {\n+        puts \"Error saving config: $err\"\n     }\n     set stuffsaved 1\n }\n-- \n2.1.1.391.g7a54a76\n"},{"id":"256958","messageId":"1425441498-29416-4-git-send-email-max@max630.net","threadId":"38698","inReplyTo":"1425441498-29416-1-git-send-email-max@max630.net","subject":"[PATCH v5 3/3] gitk: synchronize config write","fromName":"Max Kirillov","fromEmail":"max@max630.net","sentAt":"2015-03-04T03:58:18Z","receivedAt":"2015-03-04T03:58:18Z","isPatch":true,"sender":{"key":"max@max630.net","avatar":"https://avatars.githubusercontent.com/u/381560?v=4"},"body":"If several gitk instances are closed simultaneously, safestuff procedure\ncan run at the same time, resulting in a conflict which may cause losing\nof some of the instance's changes, failing the saving operation or even\ncorrupting the configuration file. This can happen, for example, at user\nsession closing, or at group closing of all instances of an application\nwhich is possible in some desktop environments.\n\nTo avoid this, make sure that only one saving operation is in progress.\nIt is guarded by existance of $config_file_tmp file. Both creating the\nfile and moving it to $config_file are atomic operations, so it should\nbe reliable.\n\nReading does not need to be syncronized, because moving is atomic\noperation, and the $config_file always refers to full and correct file.\nBut, if there is a stale $config_file_tmp file, report it at gitk start.\nIf such file is detected at saving, just report it abort the saving, as\nother saving error does.\n\nSigned-off-by: Max Kirillov <max@max630.net>\n---\n gitk | 29 ++++++++++++++++++++++++++---\n 1 file changed, 26 insertions(+), 3 deletions(-)\n\ndiff --git a/gitk b/gitk\nindex 9404d5d..b2cfd47 100755\n--- a/gitk\n+++ b/gitk\n@@ -2776,6 +2776,19 @@ proc doprogupdate {} {\n     }\n }\n \n+proc config_check_tmp_exists {tries_left} {\n+    global config_file_tmp\n+\n+    if {[file exists $config_file_tmp]} {\n+\tincr tries_left -1\n+\tif {$tries_left > 0} {\n+\t    after 100 [list config_check_tmp_exists $tries_left]\n+\t} else {\n+\t    error_popup \"Probably there is stale $config_file_tmp file; config saving is going to fail. Check if it is being used by any existing gitk process and remove it otherwise\"\n+\t}\n+    }\n+}\n+\n proc config_init_trace {name} {\n     global config_variable_changed config_variable_original\n \n@@ -2811,11 +2824,16 @@ proc savestuff {w} {\n \n     if {$stuffsaved} return\n     if {![winfo viewable .]} return\n+    set remove_tmp 0\n     if {[catch {\n-\tif {[file exists $config_file_tmp]} {\n-\t    file delete -force $config_file_tmp\n+\tset try_count 0\n+\twhile {[catch {set f [open $config_file_tmp {WRONLY CREAT EXCL}]}]} {\n+\t    if {[incr try_count] > 50} {\n+\t\terror \"Unable to write config file: $config_file_tmp exists\"\n+\t    }\n+\t    after 100\n \t}\n-\tset f [open $config_file_tmp w]\n+\tset remove_tmp 1\n \tif {$::tcl_platform(platform) eq {windows}} {\n \t    file attributes $config_file_tmp -hidden true\n \t}\n@@ -2877,9 +2895,13 @@ proc savestuff {w} {\n \tputs $f \"}\"\n \tclose $f\n \tfile rename -force $config_file_tmp $config_file\n+\tset remove_tmp 0\n     } err]} {\n         puts \"Error saving config: $err\"\n     }\n+    if {$remove_tmp} {\n+\tfile delete -force $config_file_tmp\n+    }\n     set stuffsaved 1\n }\n \n@@ -12170,6 +12192,7 @@ catch {\n     }\n     source $config_file\n }\n+config_check_tmp_exists 50\n \n set config_variables {\n     mainfont textfont uifont tabstop findmergefiles maxgraphpct maxwidth\n-- \n2.1.1.391.g7a54a76\n"},{"id":"258270","messageId":"20150322033914.GB14271@iris.ozlabs.ibm.com","threadId":"38698","inReplyTo":"1425441498-29416-1-git-send-email-max@max630.net","subject":"Re: [PATCH v5 0/3] gitk: save only changed configuration on exit","fromName":"Paul Mackerras","fromEmail":"paulus@samba.org","sentAt":"2015-03-22T03:39:15Z","receivedAt":"2015-03-22T03:39:15Z","isPatch":true,"sender":{"key":"paulus@samba.org","avatar":"https://avatars.githubusercontent.com/u/1606439?v=4"},"body":"On Wed, Mar 04, 2015 at 05:58:15AM +0200, Max Kirillov wrote:\n> The changes:\n> \n> * remove unused views_modified_names assignment\n> * use if {[catch...] to check saving error\n> * split error reporting from busy wait\n> \n> The busy wait parameters are unchanged, mostly because I did not have time yet to test them.\n\nThanks, applied, with some rewording of the commit messages, and I\nupdated the error message that is shown when a stale temporary config\nfile exists to what I suggested previously.\n\nPaul.\n"}]}