add mutexes for concurrency, added concurrency tests

This commit is contained in:
Shane C. 2024-12-03 10:59:26 -05:00
parent 9cc659b973
commit e3d512edcc
Signed by: Shane C.
GPG key ID: E46B5FEA35B22FF9
4 changed files with 52 additions and 1 deletions

View file

@ -5,7 +5,7 @@ A golang library for extracting and creating archives.
## Roadmap ## Roadmap
- [ ] Automatically detect the archive format - [ ] Automatically detect the archive format
- [ ] Add mutexes to work with concurrency - [x] Add mutexes to work with concurrency
## Supported Formats ## Supported Formats
- `.tar.gz` - `.tar.gz`

View file

@ -10,6 +10,7 @@ import (
"io/fs" "io/fs"
"os" "os"
"regexp" "regexp"
"sync"
) )
type Type string type Type string
@ -36,6 +37,7 @@ type Archive struct {
tarReader *tar.Reader // Used for anything with .tar due to how tar.Reader cannot be reset. tarReader *tar.Reader // Used for anything with .tar due to how tar.Reader cannot be reset.
files map[string]*File files map[string]*File
archiveFile *bytes.Reader archiveFile *bytes.Reader
mu sync.Mutex
} }
// Filesystem represents a standard interface for filesystems. // Filesystem represents a standard interface for filesystems.
@ -109,6 +111,8 @@ type File struct {
// It takes the path of the file in the Archive as its parameter. // It takes the path of the file in the Archive as its parameter.
// The function returns ArchiveFile and an error, if any. // The function returns ArchiveFile and an error, if any.
func (a *Archive) GetFile(path string) (*File, error) { func (a *Archive) GetFile(path string) (*File, error) {
a.mu.Lock()
defer a.mu.Unlock()
file, ok := a.files[path] file, ok := a.files[path]
if !ok { if !ok {
return nil, ErrArchiveFileNotFound return nil, ErrArchiveFileNotFound

View file

@ -10,6 +10,7 @@ import (
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
"sync"
"testing" "testing"
) )
@ -19,6 +20,43 @@ const (
var archiveRegex = regexp.MustCompile(`(?m)test[1|5]`) var archiveRegex = regexp.MustCompile(`(?m)test[1|5]`)
func TestArchiveConcurrency(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err)
testGenerateZip(t)
archive, err := Open(Zip, testArchiveBaseDir+"/test.zip")
assert.NoError(t, err)
var wg sync.WaitGroup
// Slam it, see if it breaks.
for _ = range 100_000 {
go func() {
wg.Add(1)
defer wg.Done()
file, err := archive.GetFile("test0.txt")
assert.NoError(t, err)
assert.Equal(t, "test0.txt", file.FileName)
err = file.Extract(ExtractFileOptions{
Overwrite: true,
Folder: testArchiveBaseDir + "/extracted/zip",
})
assert.NoError(t, err)
}()
}
wg.Wait()
err = os.RemoveAll(testArchiveBaseDir + "/extracted")
assert.NoError(t, err)
err = archive.Close()
assert.NoError(t, err)
}
func TestArchiveExtract(t *testing.T) { func TestArchiveExtract(t *testing.T) {
err := os.MkdirAll(testArchiveBaseDir, os.ModePerm) err := os.MkdirAll(testArchiveBaseDir, os.ModePerm)
assert.NoError(t, err) assert.NoError(t, err)
@ -59,6 +97,9 @@ func TestArchiveExtract(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
err = os.RemoveAll(testArchiveBaseDir + "/extracted")
assert.NoError(t, err)
} }
func TestArchiveZip(t *testing.T) { func TestArchiveZip(t *testing.T) {

View file

@ -22,6 +22,9 @@ type extractOptions struct {
} }
func extract(filesystem Filesystem, opts ExtractOptions, archive *Archive) error { func extract(filesystem Filesystem, opts ExtractOptions, archive *Archive) error {
archive.mu.Lock()
defer archive.mu.Unlock()
if filesystem.billyFS == nil { if filesystem.billyFS == nil {
filesystem.file = true filesystem.file = true
} }
@ -55,6 +58,9 @@ func extract(filesystem Filesystem, opts ExtractOptions, archive *Archive) error
} }
func extractFile(filesystem Filesystem, opts ExtractFileOptions, file *File) error { func extractFile(filesystem Filesystem, opts ExtractFileOptions, file *File) error {
file.archive.mu.Lock()
defer file.archive.mu.Unlock()
if filesystem.billyFS == nil { if filesystem.billyFS == nil {
filesystem.file = true filesystem.file = true
} }