From f03d77f73be72a934a99541b30a9663cc99a1733 Mon Sep 17 00:00:00 2001 From: Jordan Date: Sun, 14 Mar 2021 13:43:03 -0700 Subject: r/w map locks; concurrent safety --- crane.go | 39 ++++++++++++++++++++++++++++++++++++++- http.go | 22 ++++++++++++++++++---- templates/admin-edit.html | 10 +++++----- templates/admin.html | 6 +++--- templates/index.html | 4 ++-- templates/list.html | 2 +- 6 files changed, 67 insertions(+), 16 deletions(-) diff --git a/crane.go b/crane.go index 32e9a5f..bd5bb8a 100644 --- a/crane.go +++ b/crane.go @@ -17,6 +17,7 @@ import ( "os" "path/filepath" "strings" + "sync" "golang.org/x/net/publicsuffix" ) @@ -64,12 +65,13 @@ type Paper struct { } type Papers struct { + sync.RWMutex List map[string]map[string]*Paper Path string } type Resp struct { - Papers map[string]map[string]*Paper + Papers Papers Status string LastPaperDL string LastUsedCategory string @@ -121,6 +123,7 @@ func getPaperFileNameFromResp(resp *http.Response) string { func (papers *Papers) getUniqueName(category string, name string) string { newName := name ext := 2 + papers.RLock() for { key := filepath.Join(category, newName+".pdf") if _, exists := papers.List[category][key]; exists != true { @@ -130,6 +133,7 @@ func (papers *Papers) getUniqueName(category string, name string) string { ext++ } } + papers.RUnlock() return newName } @@ -142,6 +146,9 @@ func (papers *Papers) findPapersWalk(path string, info os.FileInfo, return nil } + papers.Lock() + defer papers.Unlock() + // derive category name (e.g. Mathematics) from directory name; used as key var category string if i, _ := os.Stat(path); i.IsDir() { @@ -246,10 +253,12 @@ func (papers *Papers) NewPaperFromDOI(doi []byte, category string) (*Paper, // if not matching, check if DOIs match (genuine duplicate) if name != uniqueName { key := filepath.Join(category, name+".pdf") + papers.RLock() if meta.DOI == papers.List[category][key].Meta.DOI { return nil, fmt.Errorf("paper %q with DOI %q already downloaded", name, string(doi)) } + papers.RUnlock() } paper.PaperName = uniqueName @@ -276,8 +285,11 @@ func (papers *Papers) NewPaperFromDOI(doi []byte, category string) (*Paper, return nil, err } paper.Meta = *meta + + papers.Lock() papers.List[category][filepath.Join(category, paper.PaperName+".pdf")] = &paper + papers.Unlock() return &paper, nil } @@ -315,8 +327,10 @@ func (papers *Papers) NewPaperFromDirectLink(resp *http.Response, meta *Meta, if err := renameFile(tmpPDF.Name(), paper.PaperPath); err != nil { return nil, err } + papers.Lock() papers.List[category][filepath.Join(category, paper.PaperName+".pdf")] = &paper + papers.Unlock() return &paper, nil } @@ -325,6 +339,7 @@ func (papers *Papers) NewPaperFromDirectLink(resp *http.Response, meta *Meta, func (papers *Papers) DeletePaper(paper string) error { // check if the category in which the paper is said to belong // exists + papers.RLock() category := filepath.Dir(paper) if _, exists := papers.List[category]; exists != true { return fmt.Errorf("category %q does not exist\n", @@ -336,9 +351,11 @@ func (papers *Papers) DeletePaper(paper string) error { return fmt.Errorf("paper %q does not exist in category %q\n", paper, category) } + papers.RUnlock() // paper and category exists and the paper belongs to the provided // category; remove it and its XML metadata + papers.Lock() if err := os.Remove(papers.List[category][paper].PaperPath); err != nil { return err } @@ -353,31 +370,40 @@ func (papers *Papers) DeletePaper(paper string) error { } } delete(papers.List[category], paper) + papers.Unlock() return nil } // DeleteCategory deletes a category and its contents from the filesystem and // the papers.List set func (papers *Papers) DeleteCategory(category string) error { + papers.RLock() if _, exists := papers.List[category]; exists != true { return fmt.Errorf("category %q does not exist in the set\n", category) } + papers.RUnlock() + + papers.Lock() if err := os.RemoveAll(filepath.Join(papers.Path, category)); err != nil { return err } + // remove subcategories (nested directories) which exist under the primary for key, _ := range papers.List { if strings.HasPrefix(key, category+"/") { delete(papers.List, key) } } + delete(papers.List, category) + papers.Unlock() return nil } // MovePaper moves a paper to the destination category on the filesystem and // the papers.List set func (papers *Papers) MovePaper(paper string, category string) error { + papers.RLock() prevCategory := filepath.Dir(paper) if _, exists := papers.List[prevCategory]; exists != true { return fmt.Errorf("category %q does not exist\n", prevCategory) @@ -393,6 +419,9 @@ func (papers *Papers) MovePaper(paper string, category string) error { return fmt.Errorf("paper %q exists in destination category %q\n", paper, category) } + papers.RUnlock() + + papers.Lock() paperDest := filepath.Join(filepath.Join(papers.Path, category), papers.List[prevCategory][paper].PaperName+".pdf") if err := os.Rename(papers.List[prevCategory][paper].PaperPath, paperDest); @@ -422,12 +451,15 @@ func (papers *Papers) MovePaper(paper string, category string) error { } } delete(papers.List[prevCategory], paper) + + papers.Unlock() return nil } // RenameCategory renames a category on the filesystem and the paper.List set func (papers *Papers) RenameCategory(oldCategory string, newCategory string) error { + papers.RLock() if _, exists := papers.List[oldCategory]; exists != true { return fmt.Errorf("category %q does not exist in the set\n", oldCategory) } @@ -438,6 +470,9 @@ func (papers *Papers) RenameCategory(oldCategory string, filepath.Join(papers.Path, newCategory)); err != nil { return err } + papers.RUnlock() + + papers.Lock() papers.List[newCategory] = make(map[string]*Paper) for k, v := range papers.List[oldCategory] { pPaperPath := filepath.Join(papers.Path, filepath.Join(newCategory, @@ -453,6 +488,8 @@ func (papers *Papers) RenameCategory(oldCategory string, } } delete(papers.List, oldCategory) + + papers.Unlock() return nil } diff --git a/http.go b/http.go index fcfd38f..ad42c2b 100644 --- a/http.go +++ b/http.go @@ -20,9 +20,12 @@ func (papers *Papers) IndexHandler(w http.ResponseWriter, r *http.Request) { filepath.Join(templateDir, "index.html"), filepath.Join(templateDir, "list.html"), ) + papers.RLock() res := Resp{ - Papers: papers.List, + Papers: *papers, } + papers.RUnlock() + t.Execute(w, &res) } @@ -33,9 +36,12 @@ func (papers *Papers) AdminHandler(w http.ResponseWriter, r *http.Request) { filepath.Join(templateDir, "layout.html"), filepath.Join(templateDir, "list.html"), ) + papers.RLock() res := Resp{ - Papers: papers.List, + Papers: *papers, } + papers.RUnlock() + if user != "" && pass != "" { username, password, ok := r.BasicAuth() if ok && user == username && pass == password { @@ -58,9 +64,12 @@ func (papers *Papers) EditHandler(w http.ResponseWriter, r *http.Request) { filepath.Join(templateDir, "layout.html"), filepath.Join(templateDir, "list.html"), ) + papers.RLock() res := Resp{ - Papers: papers.List, + Papers: *papers, } + papers.RUnlock() + if user != "" && pass != "" { username, password, ok := r.BasicAuth() if !ok || user != username || pass != password { @@ -152,7 +161,8 @@ func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) { // sanitize input; we use the category to build the path used to save // papers nc = strings.Trim(strings.Replace(nc, "..", "", -1), "/.") - res := Resp{Papers: papers.List} + + res := Resp{} // paper download, both required fields populated if len(strings.TrimSpace(p)) > 0 && len(strings.TrimSpace(c)) > 0 { @@ -194,6 +204,10 @@ func (papers *Papers) AddHandler(w http.ResponseWriter, r *http.Request) { res.Status = fmt.Sprintf("category %q added successfully", nc) } } + papers.RLock() + res.Papers = *papers + papers.RUnlock() + t.Execute(w, &res) } diff --git a/templates/admin-edit.html b/templates/admin-edit.html index 57a9aaa..7b30294 100644 --- a/templates/admin-edit.html +++ b/templates/admin-edit.html @@ -3,13 +3,13 @@

{{ .Status }}

- {{ $categoryCount := len .Papers }} + {{ $categoryCount := len .Papers.List }} {{ if gt $categoryCount 0 }}
- {{ range $category, $paper := .Papers }}[{{ $category }}] {{ end }} + {{ range $category, $paper := .Papers.List }}[{{ $category }}] {{ end }}
@@ -36,7 +36,7 @@ - {{ range $category, $papers := .Papers }} + {{ range $category, $papers := .Papers.List }} {{ end }} @@ -44,7 +44,7 @@
-{{ range $category, $papers := .Papers }} +{{ range $category, $papers := .Papers.List }} {{ $paperCount := len $papers }}
diff --git a/templates/admin.html b/templates/admin.html index 22b0e0a..05cf04f 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -14,7 +14,7 @@
- {{ $categoryCount := len .Papers }} + {{ $categoryCount := len .Papers.List }} {{ if gt $categoryCount 0 }}
@@ -25,7 +25,7 @@ {{ if $lastUsedCategory }} {{ end }} - {{ range $category, $papers := .Papers }} + {{ range $category, $papers := .Papers.List }} {{ if ne $category $lastUsedCategory }} {{ end }} @@ -39,7 +39,7 @@
{{ if gt $categoryCount 0 }}
- {{ range $category, $paper := .Papers }}[{{ $category }}] {{ end }} + {{ range $category, $paper := .Papers.List }}[{{ $category }}] {{ end }}
diff --git a/templates/index.html b/templates/index.html index 30023cd..6397671 100644 --- a/templates/index.html +++ b/templates/index.html @@ -2,10 +2,10 @@ {{ define "content" }}
-{{ $categoryCount := len .Papers }} +{{ $categoryCount := len .Papers.List }} {{ if gt $categoryCount 0 }}
- {{ range $category, $paper := .Papers }}[{{ $category }}] {{ end }} + {{ range $category, $paper := .Papers.List }}[{{ $category }}] {{ end }}
diff --git a/templates/list.html b/templates/list.html index 9e139b9..a08efd6 100644 --- a/templates/list.html +++ b/templates/list.html @@ -1,6 +1,6 @@ {{ define "list" }}
-{{ range $category, $papers := .Papers }} +{{ range $category, $papers := .Papers.List }} {{ $paperCount := len $papers }} {{ if ge $paperCount 1 }}
-- cgit v1.2.3-54-g00ecf
Manage