Skip to content

Commit

Permalink
feat: add sync
Browse files Browse the repository at this point in the history
  • Loading branch information
andeya committed Sep 25, 2022
1 parent 067c201 commit 826626b
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 0 deletions.
136 changes: 136 additions & 0 deletions sync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package gust

import "sync"

// NewMutex returns a new *Mutex.
func NewMutex[T any](data T) *Mutex[T] {
return &Mutex[T]{data: data}
}

// NewRWMutex returns a new *RWMutex.
func NewRWMutex[T any](data T) *RWMutex[T] {
return &RWMutex[T]{data: data}
}

// Mutex is a wrapper of `sync.Mutex` that holds a value.
type Mutex[T any] struct {
inner sync.Mutex
data T
}

// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex[T]) Lock() T {
m.inner.Lock()
return m.data
}

// TryLock tries to lock m and reports whether it succeeded.
//
// Note that while correct uses of TryLock do exist, they are rare,
// and use of TryLock is often a sign of a deeper problem
// in a particular use of mutexes.
func (m *Mutex[T]) TryLock() Option[T] {
if m.inner.TryLock() {
return Some(m.data)
}
return None[T]()
}

// Unlock unlocks m.
// It is a run-time error if m is not locked on entry to Unlock.
//
// A locked Mutex is not associated with a particular goroutine.
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex[T]) Unlock(newData ...T) {
if len(newData) > 0 {
m.data = newData[0]
}
m.inner.Unlock()
}

// RWMutex is a wrapper of `sync.RWMutex` that holds a value.
type RWMutex[T any] struct {
inner sync.RWMutex
data T
}

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (m *RWMutex[T]) Lock() T {
m.inner.Lock()
return m.data
}

// TryLock tries to lock rw for writing and reports whether it succeeded.
//
// Note that while correct uses of TryLock do exist, they are rare,
// and use of TryLock is often a sign of a deeper problem
func (m *RWMutex[T]) TryLock() Option[T] {
if m.inner.TryLock() {
return Some(m.data)
}
return None[T]()
}

// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock.
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (m *RWMutex[T]) Unlock(newData ...T) {
if len(newData) > 0 {
m.data = newData[0]
}
m.inner.Unlock()
}

// Happens-before relationships are indicated to the race detector via:
// - Unlock -> Lock: readerSem
// - Unlock -> RLock: readerSem
// - RUnlock -> Lock: writerSem
//
// The methods below temporarily disable handling of race synchronization
// events in order to provide the more precise model above to the race
// detector.
//
// For example, atomic.AddInt32 in RLock should not appear to provide
// acquire-release semantics, which would incorrectly synchronize racing
// readers, thus potentially missing races.

// RLock locks rw for reading.
//
// It should not be used for recursive read locking; a blocked Lock
// call excludes new readers from acquiring the lock. See the
// documentation on the RWMutex type.
func (m *RWMutex[T]) RLock() T {
m.inner.RLock()
return m.data
}

// TryRLock tries to lock rw for reading and reports whether it succeeded.
//
// Note that while correct uses of TryRLock do exist, they are rare,
// and use of TryRLock is often a sign of a deeper problem
// in a particular use of mutexes.
func (m *RWMutex[T]) TryRLock() Option[T] {
if m.inner.TryRLock() {
return Some(m.data)
}
return None[T]()
}

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (m *RWMutex[T]) RUnlock(newData ...T) {
if len(newData) > 0 {
m.data = newData[0]
}
m.inner.RUnlock()
}
18 changes: 18 additions & 0 deletions sync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gust_test

import (
"testing"

"github.com/andeya/gust"
"github.com/stretchr/testify/assert"
)

func TestMutex(t *testing.T) {
var m = gust.NewMutex(1)
assert.Equal(t, 1, m.Lock())
m.Unlock()
assert.Equal(t, 1, m.Lock())
m.Unlock(2)
assert.Equal(t, 2, m.Lock())
m.Unlock()
}

0 comments on commit 826626b

Please sign in to comment.