From be48805f636d23c9d9fa50d973481170563ef8c4 Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Tue, 15 Aug 2023 00:45:33 +0200
Subject: [PATCH] fix: deadlock #4

---
 change.go       | 46 +++++++++++++++++++---------------------
 copyable.go     | 12 ++++++-----
 devenv.nix      | 56 ++++++++++++++++++++++++-------------------------
 http-handler.go |  5 +++--
 import.go       |  5 +++--
 5 files changed, 62 insertions(+), 62 deletions(-)

diff --git a/change.go b/change.go
index 95e7e46..df2b1bd 100644
--- a/change.go
+++ b/change.go
@@ -7,25 +7,21 @@ import (
 	"github.com/r3labs/diff/v3"
 )
 
-func (s *Settings[C]) setConfigInternal(config C, lock bool) *Settings[C] {
+func (s *Settings[C]) notifyHooks(changelog diff.Changelog) {
 
-	var (
-		changelog diff.Changelog
-		err       error
-	)
-
-	if lock {
-		s.Lock()
+	if len(changelog) > 0 {
+		s.notifyPostprocessingHooks(changelog)
+		s.notifyChangeHooks(changelog)
 	}
 
-	defer func() {
+}
 
-		if len(changelog) > 0 {
-			s.notifyPostprocessingHooks(changelog)
-			s.notifyChangeHooks(changelog)
-		}
+func (s *Settings[C]) setConfigInternal(config C) (*Settings[C], diff.Changelog) {
 
-	}()
+	var (
+		changelog diff.Changelog
+		err       error
+	)
 
 	errorCount := len(s.errors)
 	defer func() {
@@ -34,21 +30,15 @@ func (s *Settings[C]) setConfigInternal(config C, lock bool) *Settings[C] {
 		}
 	}()
 
-	defer func() {
-		if lock {
-			s.Unlock()
-		}
-	}()
-
 	if err := validateConfig[C](config); err != nil {
 		s.errors = append(s.errors, err)
-		return s
+		return s, changelog
 	}
 
 	d, err := diff.NewDiffer()
 	if err != nil {
 		s.errors = append(s.errors, err)
-		return s
+		return s, changelog
 	}
 
 	d.ConvertCompatibleTypes = true
@@ -57,13 +47,19 @@ func (s *Settings[C]) setConfigInternal(config C, lock bool) *Settings[C] {
 	changelog, err = d.Diff(s.config, config)
 	if err != nil {
 		s.errors = append(s.errors, err)
-		return s
+		return s, changelog
 	}
 
 	s.config = config
-	return s
+	return s, changelog
 }
 
 func (s *Settings[C]) SetConfig(config C) *Settings[C] {
-	return s.setConfigInternal(config, true)
+	s.Lock()
+	c, l := s.setConfigInternal(config)
+	s.Unlock()
+
+	s.notifyHooks(l)
+
+	return c
 }
diff --git a/copyable.go b/copyable.go
index b579e6d..7a64e4b 100644
--- a/copyable.go
+++ b/copyable.go
@@ -7,15 +7,14 @@ import (
 	"gitlab.schukai.com/oss/libraries/go/utilities/pathfinder"
 )
 
-// // Copy implements the xflags.Copyable interface.
+// Copy implements the xflags.Copyable interface.
 func (s *Settings[C]) Copy(m map[string]any) {
 
 	if s == nil {
-		panic("struct is not initialized")
+		panic("cannot copy nil")
 	}
 
 	s.Lock()
-	defer s.Unlock()
 
 	errorCount := len(s.errors)
 	defer func() {
@@ -37,6 +36,9 @@ func (s *Settings[C]) Copy(m map[string]any) {
 		}
 	}
 
-	s.setConfigInternal(c, false)
-	
+	_, l := s.setConfigInternal(c)
+	s.Unlock()
+
+	s.notifyHooks(l)
+
 }
diff --git a/devenv.nix b/devenv.nix
index 2568f0c..0eb76b3 100644
--- a/devenv.nix
+++ b/devenv.nix
@@ -3,34 +3,34 @@
 {
 
   # https://devenv.sh/packages/
-  packages = [
+  packages = with pkgs; [
     inputs.version.defaultPackage."${builtins.currentSystem}"
-    pkgs.git
-    pkgs.gcc12
-    pkgs.go-task
-    pkgs.blackbox
-    pkgs.blackbox-terminal
-    pkgs.jq
-    pkgs.delve
-    pkgs.gdlv
-    pkgs.wget
-    pkgs.glab
-    pkgs.unixtools.xxd
-    pkgs.libffi
-    pkgs.zlib
-    pkgs.procps
-    pkgs.php81Extensions.xdebug
-    pkgs.ranger
-    pkgs.meld
-    pkgs.gnused
-    pkgs.coreutils-full
-    pkgs.gnugrep
-    pkgs.gnumake
-    pkgs.util-linux
-    pkgs.httpie
-    pkgs.netcat
-    pkgs.memcached
-    pkgs.fd    
+    git
+    gcc12
+    go-task
+    blackbox
+    blackbox-terminal
+    jq
+    delve
+    gdlv
+    wget
+    glab
+    unixtools.xxd
+    libffi
+    zlib
+    procps
+    php81Extensions.xdebug
+    ranger
+    meld
+    gnused
+    coreutils-full
+    gnugrep
+    gnumake
+    util-linux
+    httpie
+    netcat
+    memcached
+    fd    
   ];
 
 
@@ -65,7 +65,7 @@ PATH="''${PATH}":${pkgs.git}/bin/
 PATH="''${PATH}":${pkgs.gnugrep}/bin/
 PATH="''${PATH}":${inputs.version.defaultPackage."${builtins.currentSystem}"}/bin/
 
-export -f PATH
+export PATH
 
 task test
 
diff --git a/http-handler.go b/http-handler.go
index c865f42..b7c2b71 100644
--- a/http-handler.go
+++ b/http-handler.go
@@ -83,7 +83,6 @@ func (s *Settings[C]) servePost(w http.ResponseWriter, r *http.Request) {
 	}
 
 	s.Lock()
-	defer s.Unlock()
 
 	b := s.config
 	s.importStream(rs)
@@ -91,7 +90,9 @@ func (s *Settings[C]) servePost(w http.ResponseWriter, r *http.Request) {
 	x := s.config
 	s.config = b
 
-	s.setConfigInternal(x, false)
+	_, l := s.setConfigInternal(x)
+	s.Unlock()
+	s.notifyHooks(l)
 
 }
 
diff --git a/import.go b/import.go
index 486f02b..f6162ef 100644
--- a/import.go
+++ b/import.go
@@ -187,7 +187,6 @@ func (s *Settings[C]) importFiles() {
 func (s *Settings[C]) Import() *Settings[C] {
 
 	s.Lock()
-	defer s.Unlock()
 
 	errorCount := len(s.errors)
 	defer func() {
@@ -216,7 +215,9 @@ func (s *Settings[C]) Import() *Settings[C] {
 
 	x := s.config
 	s.config = defaults
-	s.setConfigInternal(x, false)
+	_, l := s.setConfigInternal(x)
+	s.Unlock()
+	s.notifyHooks(l)
 
 	return s
 }
-- 
GitLab