From 79958aa738d2e6a62cf286b6cb875bcd7b3acc99 Mon Sep 17 00:00:00 2001 From: xuri Date: Sat, 25 May 2024 14:54:59 +0800 Subject: [PATCH] This closes #1903, made GetCellStyle, SetRowVisible and GetRowVisible functions concurrency safe - Update comments of the functions and unit tests --- cell_test.go | 11 ++++++++++- col.go | 3 +++ rows.go | 19 ++++++++++++++----- styles.go | 9 ++++++--- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/cell_test.go b/cell_test.go index 7517d28f6e..ab951f415a 100644 --- a/cell_test.go +++ b/cell_test.go @@ -4,6 +4,7 @@ import ( "fmt" _ "image/jpeg" "math" + "math/rand" "os" "path/filepath" "reflect" @@ -42,6 +43,9 @@ func TestConcurrency(t *testing.T) { assert.NoError(t, err) // Concurrency set cell style assert.NoError(t, f.SetCellStyle("Sheet1", "A3", "A3", style)) + // Concurrency get cell style + _, err = f.GetCellStyle("Sheet1", "A3") + assert.NoError(t, err) // Concurrency add picture assert.NoError(t, f.AddPicture("Sheet1", "F21", filepath.Join("test", "images", "excel.jpg"), &GraphicOptions{ @@ -87,7 +91,12 @@ func TestConcurrency(t *testing.T) { // Concurrency get columns visible visible, err := f.GetColVisible("Sheet1", "A") assert.NoError(t, err) - assert.Equal(t, true, visible) + assert.True(t, visible) + // Concurrency set row visible + assert.NoError(t, f.SetRowVisible("Sheet1", 1+rand.Intn(1000), false)) + // Concurrency get row visible + _, err = f.GetRowVisible("Sheet1", 1+rand.Intn(1000)) + assert.NoError(t, err) // Concurrency add data validation dv := NewDataValidation(true) dv.Sqref = fmt.Sprintf("A%d:B%d", val, val) diff --git a/col.go b/col.go index 68ffc70cc0..927959a3f4 100644 --- a/col.go +++ b/col.go @@ -294,10 +294,13 @@ func (f *File) SetColVisible(sheet, columns string, visible bool) error { if err != nil { return err } + f.mu.Lock() ws, err := f.workSheetReader(sheet) if err != nil { + f.mu.Unlock() return err } + f.mu.Unlock() ws.mu.Lock() defer ws.mu.Unlock() colData := xlsxCol{ diff --git a/rows.go b/rows.go index d43a015040..3084a50a56 100644 --- a/rows.go +++ b/rows.go @@ -480,37 +480,46 @@ func (f *File) sharedStringsReader() (*xlsxSST, error) { } // SetRowVisible provides a function to set visible of a single row by given -// worksheet name and Excel row number. For example, hide row 2 in Sheet1: +// worksheet name and row number. This function is concurrency safe. For +// example, hide row 2 in Sheet1: // // err := f.SetRowVisible("Sheet1", 2, false) func (f *File) SetRowVisible(sheet string, row int, visible bool) error { if row < 1 { return newInvalidRowNumberError(row) } - + f.mu.Lock() ws, err := f.workSheetReader(sheet) if err != nil { + f.mu.Unlock() return err } + f.mu.Unlock() + ws.mu.Lock() + defer ws.mu.Unlock() ws.prepareSheetXML(0, row) ws.SheetData.Row[row-1].Hidden = !visible return nil } // GetRowVisible provides a function to get visible of a single row by given -// worksheet name and Excel row number. For example, get visible state of row -// 2 in Sheet1: +// worksheet name and row number. This function is concurrency safe. For +// example, get visible state of row 2 in Sheet1: // // visible, err := f.GetRowVisible("Sheet1", 2) func (f *File) GetRowVisible(sheet string, row int) (bool, error) { if row < 1 { return false, newInvalidRowNumberError(row) } - + f.mu.Lock() ws, err := f.workSheetReader(sheet) if err != nil { + f.mu.Unlock() return false, err } + f.mu.Unlock() + ws.mu.Lock() + defer ws.mu.Unlock() if row > len(ws.SheetData.Row) { return false, nil } diff --git a/styles.go b/styles.go index ccd758a6c2..7aee0ff575 100644 --- a/styles.go +++ b/styles.go @@ -2186,19 +2186,22 @@ func setCellXfs(style *xlsxStyleSheet, fontID, numFmtID, fillID, borderID int, a } // GetCellStyle provides a function to get cell style index by given worksheet -// name and cell reference. +// name and cell reference. This function is concurrency safe. func (f *File) GetCellStyle(sheet, cell string) (int, error) { + f.mu.Lock() ws, err := f.workSheetReader(sheet) if err != nil { + f.mu.Unlock() return 0, err } + f.mu.Unlock() + ws.mu.Lock() + defer ws.mu.Unlock() col, row, err := CellNameToCoordinates(cell) if err != nil { return 0, err } ws.prepareSheetXML(col, row) - ws.mu.Lock() - defer ws.mu.Unlock() return ws.prepareCellStyle(col, row, ws.SheetData.Row[row-1].C[col-1].S), err }