Skip to content

Commit

Permalink
Add mutex package
Browse files Browse the repository at this point in the history
  • Loading branch information
Zamony committed Feb 24, 2024
1 parent b5e1b93 commit 668e9df
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 6 deletions.
33 changes: 27 additions & 6 deletions sync/mutex/mutex.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,58 @@
package rwmutex
package mutex

import (
"context"
"sync"
)

type Mutex struct {
// A RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers or a single writer.
// The zero value for a RWMutex is an unlocked mutex.
//
// A RWMutex must not be copied after first use.
type RWMutex struct {
mu sync.RWMutex
}

func (m *Mutex) TryLock(ctx context.Context) error {
// TryLock waits to lock RWMutex for writing.
// A context error is returned on canceled context.
func (m *RWMutex) TryLock(ctx context.Context) error {
if m.mu.TryLock() {
return nil
}

return wait(ctx, &m.mu)
}

func (m *Mutex) TryRLock(ctx context.Context) error {
// TryRLock waits to lock RWMutex for reading.
// A context error is returned on canceled context.
func (m *RWMutex) TryRLock(ctx context.Context) error {
if m.mu.TryRLock() {
return nil
}
return wait(ctx, (&m.mu).RLocker())
}

func (m *Mutex) Lock() {
// Lock locks RWMutex for writing.
func (m *RWMutex) Lock() {
m.mu.Lock()
}

func (m *Mutex) Unlock() {
// Unlock unlocks RWMutex for writing.
func (m *RWMutex) Unlock() {
m.mu.Unlock()
}

// RLock locks RWMutex for reading.
func (m *RWMutex) RLock() {
m.mu.RLock()
}

// RUnlock undoes a single RLock call.
func (m *RWMutex) RUnlock() {
m.mu.RUnlock()
}

func wait(ctx context.Context, mu sync.Locker) error {
done := make(chan struct{})
go func() {
Expand Down
63 changes: 63 additions & 0 deletions sync/mutex/mutex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package mutex_test

import (
"context"
"sync"
"testing"
"time"

"github.com/Zamony/go/sync/mutex"
)

const timeout = 10 * time.Millisecond

func TestMutexContext(t *testing.T) {
t.Parallel()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
t.Cleanup(cancel)

var mu mutex.RWMutex
mu.Lock()
defer mu.Unlock()

var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
if err := mu.TryLock(ctx); err != ctx.Err() {
t.Error("lock", err)
}
}()
go func() {
defer wg.Done()
if err := mu.TryRLock(ctx); err != ctx.Err() {
t.Error("rlock", err)
}
}()

wg.Wait()
}

func TestMutexLock(t *testing.T) {
t.Parallel()

ctx := context.Background()
var mu mutex.RWMutex
if err := mu.TryLock(ctx); err != nil {
t.Error("lock", err)
}

var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
if err := mu.TryRLock(ctx); err != nil {
t.Error("rlock", err)
}
mu.RUnlock()
}()

time.Sleep(timeout)
mu.Unlock()
wg.Wait()
}

0 comments on commit 668e9df

Please sign in to comment.