From 76f5fe8981793e757a29e8a6e342dc80cb840dfe Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Wed, 28 Sep 2022 23:20:45 +1000 Subject: [PATCH 01/11] fix: concurrent access to variable must be under mutex --- extendeddatacrossword.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index 52515db..e745af1 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "sync" "golang.org/x/sync/errgroup" ) @@ -83,6 +84,8 @@ func (eds *ExtendedDataSquare) solveCrossword( // Track if a single iteration of this loop made progress progressMade := false + var mut sync.Mutex + // Loop through every row and column, attempt to rebuild each row or column if incomplete for i := 0; i < int(eds.width); i++ { i := i @@ -93,6 +96,8 @@ func (eds *ExtendedDataSquare) solveCrossword( return err } + mut.Lock() + defer mut.Unlock() solved = solved && solvedRow progressMade = progressMade || progressMadeRow return nil @@ -104,6 +109,8 @@ func (eds *ExtendedDataSquare) solveCrossword( return err } + mut.Lock() + defer mut.Unlock() solved = solved && solvedCol progressMade = progressMade || progressMadeCol return nil From 3ad1f1e6e801910ea047bfeab0c75a11a13bfcbf Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Wed, 28 Sep 2022 23:21:29 +1000 Subject: [PATCH 02/11] fix: wait for all crossword rows to be solved before attempting to solve crossword columns --- extendeddatacrossword.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index e745af1..67871a3 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -85,12 +85,15 @@ func (eds *ExtendedDataSquare) solveCrossword( progressMade := false var mut sync.Mutex + var wg sync.WaitGroup + wg.Add(int(eds.width)) // Loop through every row and column, attempt to rebuild each row or column if incomplete for i := 0; i < int(eds.width); i++ { i := i errs.Go(func() error { + defer wg.Done() solvedRow, progressMadeRow, err := eds.solveCrosswordRow(i, rowRoots, colRoots) if err != nil { return err @@ -104,6 +107,7 @@ func (eds *ExtendedDataSquare) solveCrossword( }) errs.Go(func() error { + wg.Wait() solvedCol, progressMadeCol, err := eds.solveCrosswordCol(i, rowRoots, colRoots) if err != nil { return err From f9e58e927316e4f418edcb67e0b5a553680e4b3c Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 00:20:45 +1000 Subject: [PATCH 03/11] chore: remove wait group and move concurrent execution of solveCrosswordCol to separate loop --- extendeddatacrossword.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index 67871a3..16aafc6 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -85,15 +85,11 @@ func (eds *ExtendedDataSquare) solveCrossword( progressMade := false var mut sync.Mutex - var wg sync.WaitGroup - wg.Add(int(eds.width)) - // Loop through every row and column, attempt to rebuild each row or column if incomplete for i := 0; i < int(eds.width); i++ { i := i errs.Go(func() error { - defer wg.Done() solvedRow, progressMadeRow, err := eds.solveCrosswordRow(i, rowRoots, colRoots) if err != nil { return err @@ -106,8 +102,15 @@ func (eds *ExtendedDataSquare) solveCrossword( return nil }) + } + if err := errs.Wait(); err != nil { + return err + } + + for i := 0; i < int(eds.width); i++ { + i := i + errs.Go(func() error { - wg.Wait() solvedCol, progressMadeCol, err := eds.solveCrosswordCol(i, rowRoots, colRoots) if err != nil { return err @@ -120,7 +123,6 @@ func (eds *ExtendedDataSquare) solveCrossword( return nil }) } - if err := errs.Wait(); err != nil { return err } From a4183f1fd024ba0aa5e0b2f819da2f625fa839ea Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 01:04:31 +1000 Subject: [PATCH 04/11] feat: make datasquare thread-safe --- datasquare.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/datasquare.go b/datasquare.go index accad9f..67014a1 100644 --- a/datasquare.go +++ b/datasquare.go @@ -255,6 +255,9 @@ func (ds *dataSquare) getColRoot(y uint) []byte { // GetCell returns a copy of a specific cell. func (ds *dataSquare) GetCell(x uint, y uint) []byte { + ds.dataMutex.Lock() + defer ds.dataMutex.Unlock() + if ds.squareRow[x][y] == nil { return nil } @@ -266,6 +269,9 @@ func (ds *dataSquare) GetCell(x uint, y uint) []byte { // SetCell sets a specific cell. Cell to set must be `nil`. // Panics if attempting to set a cell that is not `nil`. func (ds *dataSquare) SetCell(x uint, y uint, newChunk []byte) { + ds.dataMutex.Lock() + defer ds.dataMutex.Unlock() + if ds.squareRow[x][y] != nil { panic(fmt.Sprintf("cannot set cell (%d, %d) as it already has a value %x", x, y, ds.squareRow[x][y])) } @@ -276,6 +282,9 @@ func (ds *dataSquare) SetCell(x uint, y uint, newChunk []byte) { // setCell sets a specific cell. func (ds *dataSquare) setCell(x uint, y uint, newChunk []byte) { + ds.dataMutex.Lock() + defer ds.dataMutex.Unlock() + ds.squareRow[x][y] = newChunk ds.squareCol[y][x] = newChunk ds.resetRoots() @@ -283,6 +292,9 @@ func (ds *dataSquare) setCell(x uint, y uint, newChunk []byte) { // Flattened returns the concatenated rows of the data square. func (ds *dataSquare) Flattened() [][]byte { + ds.dataMutex.Lock() + defer ds.dataMutex.Unlock() + flattened := [][]byte(nil) for _, data := range ds.squareRow { flattened = append(flattened, data...) From c469943d55314b72dfedd4fa7d684db74e79dd39 Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 01:05:21 +1000 Subject: [PATCH 05/11] chore: initialize array of mutexes that can be used to lock independent rows/columns --- extendeddatasquare.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extendeddatasquare.go b/extendeddatasquare.go index 88df96f..6a625d7 100644 --- a/extendeddatasquare.go +++ b/extendeddatasquare.go @@ -5,6 +5,7 @@ import ( "bytes" "context" "errors" + "sync" "golang.org/x/sync/errgroup" ) @@ -14,6 +15,7 @@ type ExtendedDataSquare struct { *dataSquare codec Codec originalDataWidth uint + rowColMutex []sync.Mutex } // ComputeExtendedDataSquare computes the extended data square for some chunks of data. @@ -31,7 +33,7 @@ func ComputeExtendedDataSquare( return nil, err } - eds := ExtendedDataSquare{dataSquare: ds, codec: codec} + eds := ExtendedDataSquare{dataSquare: ds, codec: codec, rowColMutex: make([]sync.Mutex, ds.width)} err = eds.erasureExtendSquare(codec) if err != nil { return nil, err @@ -55,7 +57,7 @@ func ImportExtendedDataSquare( return nil, err } - eds := ExtendedDataSquare{dataSquare: ds, codec: codec} + eds := ExtendedDataSquare{dataSquare: ds, codec: codec, rowColMutex: make([]sync.Mutex, ds.width)} if eds.width%2 != 0 { return nil, errors.New("square width must be even") } From b43d91223cb63e55dfa0a72f75f7994f3370188f Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 01:09:04 +1000 Subject: [PATCH 06/11] feat: make solveCrossword(Row/Col) thread safe by locking read/write to individual rows/columns --- extendeddatacrossword.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index 16aafc6..d148d8e 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -186,6 +186,8 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( if col[r] != nil { continue // not newly completed } + + eds.rowColMutex[c].Lock() col[r] = rebuiltShares[c] if noMissingData(col) { // not completed err := eds.verifyAgainstColRoots(colRoots, uint(c), col) @@ -193,11 +195,14 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( return false, false, err } } + eds.rowColMutex[c].Unlock() } // Insert rebuilt shares into square. for c, s := range rebuiltShares { + eds.rowColMutex[c].Lock() eds.setCell(uint(r), uint(c), s) + eds.rowColMutex[c].Unlock() } return true, true, nil @@ -248,6 +253,8 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( if row[c] != nil { continue // not newly completed } + + eds.rowColMutex[r].Lock() row[c] = rebuiltShares[r] if noMissingData(row) { // not completed err := eds.verifyAgainstRowRoots(rowRoots, uint(r), row) @@ -255,11 +262,14 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( return false, false, err } } + eds.rowColMutex[r].Unlock() } // Insert rebuilt shares into square. for r, s := range rebuiltShares { + eds.rowColMutex[r].Lock() eds.setCell(uint(r), uint(c), s) + eds.rowColMutex[r].Unlock() } return true, true, nil From 97a10c885d54893a9b7a1c499f1399315947fc2f Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 01:10:44 +1000 Subject: [PATCH 07/11] chore: give mutex a more descriptive name --- extendeddatacrossword.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index d148d8e..94b0c85 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -84,7 +84,7 @@ func (eds *ExtendedDataSquare) solveCrossword( // Track if a single iteration of this loop made progress progressMade := false - var mut sync.Mutex + var trackerMutex sync.Mutex // Loop through every row and column, attempt to rebuild each row or column if incomplete for i := 0; i < int(eds.width); i++ { i := i @@ -95,8 +95,8 @@ func (eds *ExtendedDataSquare) solveCrossword( return err } - mut.Lock() - defer mut.Unlock() + trackerMutex.Lock() + defer trackerMutex.Unlock() solved = solved && solvedRow progressMade = progressMade || progressMadeRow return nil @@ -116,8 +116,8 @@ func (eds *ExtendedDataSquare) solveCrossword( return err } - mut.Lock() - defer mut.Unlock() + trackerMutex.Lock() + defer trackerMutex.Unlock() solved = solved && solvedCol progressMade = progressMade || progressMadeCol return nil From de97176ce18de4466d6413be561c5411bf4f9731 Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 01:20:42 +1000 Subject: [PATCH 08/11] chore: revert "feat: make datasquare thread-safe" This reverts commit a4183f1fd024ba0aa5e0b2f819da2f625fa839ea. --- datasquare.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/datasquare.go b/datasquare.go index 67014a1..accad9f 100644 --- a/datasquare.go +++ b/datasquare.go @@ -255,9 +255,6 @@ func (ds *dataSquare) getColRoot(y uint) []byte { // GetCell returns a copy of a specific cell. func (ds *dataSquare) GetCell(x uint, y uint) []byte { - ds.dataMutex.Lock() - defer ds.dataMutex.Unlock() - if ds.squareRow[x][y] == nil { return nil } @@ -269,9 +266,6 @@ func (ds *dataSquare) GetCell(x uint, y uint) []byte { // SetCell sets a specific cell. Cell to set must be `nil`. // Panics if attempting to set a cell that is not `nil`. func (ds *dataSquare) SetCell(x uint, y uint, newChunk []byte) { - ds.dataMutex.Lock() - defer ds.dataMutex.Unlock() - if ds.squareRow[x][y] != nil { panic(fmt.Sprintf("cannot set cell (%d, %d) as it already has a value %x", x, y, ds.squareRow[x][y])) } @@ -282,9 +276,6 @@ func (ds *dataSquare) SetCell(x uint, y uint, newChunk []byte) { // setCell sets a specific cell. func (ds *dataSquare) setCell(x uint, y uint, newChunk []byte) { - ds.dataMutex.Lock() - defer ds.dataMutex.Unlock() - ds.squareRow[x][y] = newChunk ds.squareCol[y][x] = newChunk ds.resetRoots() @@ -292,9 +283,6 @@ func (ds *dataSquare) setCell(x uint, y uint, newChunk []byte) { // Flattened returns the concatenated rows of the data square. func (ds *dataSquare) Flattened() [][]byte { - ds.dataMutex.Lock() - defer ds.dataMutex.Unlock() - flattened := [][]byte(nil) for _, data := range ds.squareRow { flattened = append(flattened, data...) From 5b9e7659d141bf2805a50183f90bf588dbf41840 Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Thu, 29 Sep 2022 01:34:53 +1000 Subject: [PATCH 09/11] chore: change mutex name to statusMutex --- extendeddatacrossword.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index 94b0c85..83c4b2c 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -84,7 +84,7 @@ func (eds *ExtendedDataSquare) solveCrossword( // Track if a single iteration of this loop made progress progressMade := false - var trackerMutex sync.Mutex + var statusMutex sync.Mutex // Loop through every row and column, attempt to rebuild each row or column if incomplete for i := 0; i < int(eds.width); i++ { i := i @@ -95,8 +95,8 @@ func (eds *ExtendedDataSquare) solveCrossword( return err } - trackerMutex.Lock() - defer trackerMutex.Unlock() + statusMutex.Lock() + defer statusMutex.Unlock() solved = solved && solvedRow progressMade = progressMade || progressMadeRow return nil @@ -116,8 +116,8 @@ func (eds *ExtendedDataSquare) solveCrossword( return err } - trackerMutex.Lock() - defer trackerMutex.Unlock() + statusMutex.Lock() + defer statusMutex.Unlock() solved = solved && solvedCol progressMade = progressMade || progressMadeCol return nil From 33f11746cbd03abb459e592d261f99a3cdb2acd5 Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Sat, 22 Oct 2022 01:08:32 +1100 Subject: [PATCH 10/11] chore: move row/col mutex to solveCrossword method --- extendeddatacrossword.go | 24 ++++++++++++++---------- extendeddatasquare.go | 7 ++----- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index 83c4b2c..a96144a 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -75,6 +75,8 @@ func (eds *ExtendedDataSquare) solveCrossword( rowRoots [][]byte, colRoots [][]byte, ) error { + rowColMutex := make([]sync.Mutex, eds.width) + // Keep repeating until the square is solved for { errs, _ := errgroup.WithContext(context.Background()) @@ -90,7 +92,7 @@ func (eds *ExtendedDataSquare) solveCrossword( i := i errs.Go(func() error { - solvedRow, progressMadeRow, err := eds.solveCrosswordRow(i, rowRoots, colRoots) + solvedRow, progressMadeRow, err := eds.solveCrosswordRow(i, rowRoots, colRoots, rowColMutex) if err != nil { return err } @@ -111,7 +113,7 @@ func (eds *ExtendedDataSquare) solveCrossword( i := i errs.Go(func() error { - solvedCol, progressMadeCol, err := eds.solveCrosswordCol(i, rowRoots, colRoots) + solvedCol, progressMadeCol, err := eds.solveCrosswordCol(i, rowRoots, colRoots, rowColMutex) if err != nil { return err } @@ -151,6 +153,7 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( r int, rowRoots [][]byte, colRoots [][]byte, + rowColMutex []sync.Mutex, ) (bool, bool, error) { isComplete := noMissingData(eds.row(uint(r))) if isComplete { @@ -187,7 +190,7 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( continue // not newly completed } - eds.rowColMutex[c].Lock() + rowColMutex[c].Lock() col[r] = rebuiltShares[c] if noMissingData(col) { // not completed err := eds.verifyAgainstColRoots(colRoots, uint(c), col) @@ -195,14 +198,14 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( return false, false, err } } - eds.rowColMutex[c].Unlock() + rowColMutex[c].Unlock() } // Insert rebuilt shares into square. for c, s := range rebuiltShares { - eds.rowColMutex[c].Lock() + rowColMutex[c].Lock() eds.setCell(uint(r), uint(c), s) - eds.rowColMutex[c].Unlock() + rowColMutex[c].Unlock() } return true, true, nil @@ -217,6 +220,7 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( c int, rowRoots [][]byte, colRoots [][]byte, + rowColMutex []sync.Mutex, ) (bool, bool, error) { isComplete := noMissingData(eds.col(uint(c))) if isComplete { @@ -254,7 +258,7 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( continue // not newly completed } - eds.rowColMutex[r].Lock() + rowColMutex[r].Lock() row[c] = rebuiltShares[r] if noMissingData(row) { // not completed err := eds.verifyAgainstRowRoots(rowRoots, uint(r), row) @@ -262,14 +266,14 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( return false, false, err } } - eds.rowColMutex[r].Unlock() + rowColMutex[r].Unlock() } // Insert rebuilt shares into square. for r, s := range rebuiltShares { - eds.rowColMutex[r].Lock() + rowColMutex[r].Lock() eds.setCell(uint(r), uint(c), s) - eds.rowColMutex[r].Unlock() + rowColMutex[r].Unlock() } return true, true, nil diff --git a/extendeddatasquare.go b/extendeddatasquare.go index 6a625d7..2f29868 100644 --- a/extendeddatasquare.go +++ b/extendeddatasquare.go @@ -5,8 +5,6 @@ import ( "bytes" "context" "errors" - "sync" - "golang.org/x/sync/errgroup" ) @@ -15,7 +13,6 @@ type ExtendedDataSquare struct { *dataSquare codec Codec originalDataWidth uint - rowColMutex []sync.Mutex } // ComputeExtendedDataSquare computes the extended data square for some chunks of data. @@ -33,7 +30,7 @@ func ComputeExtendedDataSquare( return nil, err } - eds := ExtendedDataSquare{dataSquare: ds, codec: codec, rowColMutex: make([]sync.Mutex, ds.width)} + eds := ExtendedDataSquare{dataSquare: ds, codec: codec} err = eds.erasureExtendSquare(codec) if err != nil { return nil, err @@ -57,7 +54,7 @@ func ImportExtendedDataSquare( return nil, err } - eds := ExtendedDataSquare{dataSquare: ds, codec: codec, rowColMutex: make([]sync.Mutex, ds.width)} + eds := ExtendedDataSquare{dataSquare: ds, codec: codec} if eds.width%2 != 0 { return nil, errors.New("square width must be even") } From d7e7aa4b8a6bf024fc7c0ee872b3a60f5666dd47 Mon Sep 17 00:00:00 2001 From: Rahul Ghangas Date: Sat, 22 Oct 2022 01:55:17 +1100 Subject: [PATCH 11/11] chore: capture logic of mutating eds under single mutex --- extendeddatacrossword.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/extendeddatacrossword.go b/extendeddatacrossword.go index a96144a..d83a765 100644 --- a/extendeddatacrossword.go +++ b/extendeddatacrossword.go @@ -75,7 +75,7 @@ func (eds *ExtendedDataSquare) solveCrossword( rowRoots [][]byte, colRoots [][]byte, ) error { - rowColMutex := make([]sync.Mutex, eds.width) + var edsMutationMutex sync.Mutex // Keep repeating until the square is solved for { @@ -92,7 +92,7 @@ func (eds *ExtendedDataSquare) solveCrossword( i := i errs.Go(func() error { - solvedRow, progressMadeRow, err := eds.solveCrosswordRow(i, rowRoots, colRoots, rowColMutex) + solvedRow, progressMadeRow, err := eds.solveCrosswordRow(i, rowRoots, colRoots, &edsMutationMutex) if err != nil { return err } @@ -113,7 +113,7 @@ func (eds *ExtendedDataSquare) solveCrossword( i := i errs.Go(func() error { - solvedCol, progressMadeCol, err := eds.solveCrosswordCol(i, rowRoots, colRoots, rowColMutex) + solvedCol, progressMadeCol, err := eds.solveCrosswordCol(i, rowRoots, colRoots, &edsMutationMutex) if err != nil { return err } @@ -153,7 +153,7 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( r int, rowRoots [][]byte, colRoots [][]byte, - rowColMutex []sync.Mutex, + edsMutationMutex *sync.Mutex, ) (bool, bool, error) { isComplete := noMissingData(eds.row(uint(r))) if isComplete { @@ -183,6 +183,7 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( return false, false, err } + edsMutationMutex.Lock() // Check that newly completed orthogonal vectors match their new merkle roots for c := 0; c < int(eds.width); c++ { col := eds.col(uint(c)) @@ -190,7 +191,6 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( continue // not newly completed } - rowColMutex[c].Lock() col[r] = rebuiltShares[c] if noMissingData(col) { // not completed err := eds.verifyAgainstColRoots(colRoots, uint(c), col) @@ -198,15 +198,13 @@ func (eds *ExtendedDataSquare) solveCrosswordRow( return false, false, err } } - rowColMutex[c].Unlock() } // Insert rebuilt shares into square. for c, s := range rebuiltShares { - rowColMutex[c].Lock() eds.setCell(uint(r), uint(c), s) - rowColMutex[c].Unlock() } + edsMutationMutex.Unlock() return true, true, nil } @@ -220,7 +218,7 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( c int, rowRoots [][]byte, colRoots [][]byte, - rowColMutex []sync.Mutex, + edsMutationMutex *sync.Mutex, ) (bool, bool, error) { isComplete := noMissingData(eds.col(uint(c))) if isComplete { @@ -251,6 +249,7 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( return false, false, err } + edsMutationMutex.Lock() // Check that newly completed orthogonal vectors match their new merkle roots for r := 0; r < int(eds.width); r++ { row := eds.row(uint(r)) @@ -258,7 +257,6 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( continue // not newly completed } - rowColMutex[r].Lock() row[c] = rebuiltShares[r] if noMissingData(row) { // not completed err := eds.verifyAgainstRowRoots(rowRoots, uint(r), row) @@ -266,15 +264,13 @@ func (eds *ExtendedDataSquare) solveCrosswordCol( return false, false, err } } - rowColMutex[r].Unlock() } // Insert rebuilt shares into square. for r, s := range rebuiltShares { - rowColMutex[r].Lock() eds.setCell(uint(r), uint(c), s) - rowColMutex[r].Unlock() } + edsMutationMutex.Unlock() return true, true, nil }