lock API

lock

package

API reference for the lock package.

F
function

TestInMemoryLocker_TryLock

Parameters

core/lock/memory_test.go:10-28
func TestInMemoryLocker_TryLock(t *testing.T)

{
	l := NewInMemoryLocker()
	lease, ok, err := l.TryLock(context.Background(), "key1", 0)
	if err != nil {
		t.Fatalf("TryLock failed: %v", err)
	}
	if !ok {
		t.Fatal("expected lock acquired")
	}
	defer lease.Release(context.Background())

	_, ok, err = l.TryLock(context.Background(), "key1", 0)
	if err != nil {
		t.Fatalf("TryLock second call: %v", err)
	}
	if ok {
		t.Fatal("expected lock not acquired (already held)")
	}
}
F
function

TestInMemoryLocker_Release

Parameters

core/lock/memory_test.go:30-46
func TestInMemoryLocker_Release(t *testing.T)

{
	l := NewInMemoryLocker()
	lease, _, _ := l.TryLock(context.Background(), "k", 0)

	err := lease.Release(context.Background())
	if err != nil {
		t.Fatalf("Release failed: %v", err)
	}

	_, ok, err := l.TryLock(context.Background(), "k", 0)
	if err != nil {
		t.Fatalf("TryLock after release: %v", err)
	}
	if !ok {
		t.Fatal("expected lock acquired after release")
	}
}
F
function

TestInMemoryLocker_Acquire

Parameters

core/lock/memory_test.go:48-69
func TestInMemoryLocker_Acquire(t *testing.T)

{
	l := NewInMemoryLocker()

	var wg sync.WaitGroup
	wg.Add(1)
	go func() {
		defer wg.Done()
		lease, _, _ := l.TryLock(context.Background(), "shared", 0)
		time.Sleep(50 * time.Millisecond)
		lease.Release(context.Background())
	}()

	time.Sleep(5 * time.Millisecond)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()

	_, err := l.Acquire(ctx, "shared", 0)
	if err != nil {
		t.Fatalf("Acquire failed: %v", err)
	}
	wg.Wait()
}
F
function

TestInMemoryLocker_AcquireContextCancel

Parameters

core/lock/memory_test.go:71-83
func TestInMemoryLocker_AcquireContextCancel(t *testing.T)

{
	l := NewInMemoryLocker()
	lease, _, _ := l.TryLock(context.Background(), "blocked", 0)
	defer lease.Release(context.Background())

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
	defer cancel()

	_, err := l.Acquire(ctx, "blocked", 0)
	if err == nil {
		t.Fatal("expected error from cancelled context")
	}
}
F
function

TestInMemoryLocker_DifferentKeys

Parameters

core/lock/memory_test.go:85-94
func TestInMemoryLocker_DifferentKeys(t *testing.T)

{
	l := NewInMemoryLocker()
	lease1, ok1, _ := l.TryLock(context.Background(), "a", 0)
	lease2, ok2, _ := l.TryLock(context.Background(), "b", 0)
	defer lease1.Release(context.Background())
	defer lease2.Release(context.Background())
	if !ok1 || !ok2 {
		t.Fatal("different keys should not conflict")
	}
}
F
function

TestInMemoryLocker_TTLReleasesLock

Parameters

core/lock/memory_test.go:96-110
func TestInMemoryLocker_TTLReleasesLock(t *testing.T)

{
	l := NewInMemoryLocker()
	_, ok, err := l.TryLock(context.Background(), "expiring", 10*time.Millisecond)
	if err != nil || !ok {
		t.Fatalf("TryLock() = %v, %v, want true, nil", ok, err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	lease, err := l.Acquire(ctx, "expiring", 0)
	if err != nil {
		t.Fatalf("Acquire() after TTL: %v", err)
	}
	defer lease.Release(context.Background())
}
F
function

TestInMemoryLocker_ExpiredOwnerDoesNotReleaseNewLock

Parameters

core/lock/memory_test.go:112-139
func TestInMemoryLocker_ExpiredOwnerDoesNotReleaseNewLock(t *testing.T)

{
	l := NewInMemoryLocker()
	first, ok, err := l.TryLock(context.Background(), "reused", 40*time.Millisecond)
	if err != nil || !ok {
		t.Fatalf("first TryLock() = %v, %v", ok, err)
	}
	if err := first.Release(context.Background()); err != nil {
		t.Fatal(err)
	}
	second, ok, err := l.TryLock(context.Background(), "reused", 0)
	if err != nil || !ok {
		t.Fatalf("second TryLock() = %v, %v", ok, err)
	}
	if err := first.Release(context.Background()); err != nil {
		t.Fatal(err)
	}
	time.Sleep(60 * time.Millisecond)
	_, ok, err = l.TryLock(context.Background(), "reused", 0)
	if err != nil {
		t.Fatal(err)
	}
	if ok {
		t.Fatal("expired owner released a newer lock")
	}
	if err := second.Release(context.Background()); err != nil {
		t.Fatal(err)
	}
}
I
interface

Lease

Locker defines the interface for distributed or local locks.

core/lock/locker.go:18-21
type Lease interface

Example

// Assuming a RedisLocker implementation
locker := NewRedisLocker(redisClient)
lease, err := locker.Acquire(ctx, "my-resource", 5*time.Second)
if err == nil {
	defer lease.Release(ctx)
}

Methods

Key
Method

Returns

string
func Key(...)
Release
Method

Parameters

Returns

error
func Release(...)
I
interface

Locker

core/lock/locker.go:23-30
type Locker interface

Methods

Acquire
Method

Parameters

key string

Returns

error
func Acquire(...)
TryLock
Method

Parameters

key string

Returns

bool
error
func TryLock(...)
S
struct
Implements: Locker

InMemoryLocker

InMemoryLocker implements Locker using in-process mutexes.

core/lock/memory.go:10-14
type InMemoryLocker struct

Methods

Acquire
Method

Parameters

key string

Returns

error
func (*InMemoryLocker) Acquire(ctx context.Context, key string, ttl time.Duration) (Lease, error)
{
	ticker := time.NewTicker(10 * time.Millisecond)
	defer ticker.Stop()

	for {
		lease, ok, err := l.TryLock(ctx, key, ttl)
		if err != nil {
			return nil, err
		}
		if ok {
			return lease, nil
		}
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		case <-ticker.C:
		}
	}
}
TryLock
Method

Parameters

key string

Returns

bool
error
func (*InMemoryLocker) TryLock(ctx context.Context, key string, ttl time.Duration) (Lease, bool, error)
{
	if err := ctx.Err(); err != nil {
		return nil, false, err
	}
	l.mu.Lock()
	if _, exists := l.locks[key]; exists {
		l.mu.Unlock()
		return nil, false, nil
	}
	l.next++
	owner := l.next
	state := &lockState{owner: owner}
	l.locks[key] = state
	if ttl > 0 {
		state.timer = time.AfterFunc(ttl, func() {
			l.release(key, owner)
		})
	}
	l.mu.Unlock()
	return &memoryLease{locker: l, key: key, owner: owner}, true, nil
}
release
Method

Parameters

key string
owner uint64
func (*InMemoryLocker) release(key string, owner uint64)
{
	l.mu.Lock()
	defer l.mu.Unlock()
	state, ok := l.locks[key]
	if !ok || state.owner != owner {
		return
	}
	if state.timer != nil {
		state.timer.Stop()
	}
	delete(l.locks, key)
}

Fields

Name Type Description
mu sync.Mutex
locks map[string]*lockState
next uint64
S
struct

lockState

core/lock/memory.go:16-19
type lockState struct

Fields

Name Type Description
owner uint64
timer *time.Timer
S
struct

memoryLease

core/lock/memory.go:21-26
type memoryLease struct

Methods

Key
Method

Returns

string
func (*memoryLease) Key() string
{
	return l.key
}
Release
Method

Parameters

Returns

error
func (*memoryLease) Release(_ context.Context) error
{
	l.once.Do(func() {
		l.locker.release(l.key, l.owner)
	})
	return nil
}

Fields

Name Type Description
locker *InMemoryLocker
key string
owner uint64
once sync.Once
F
function

NewInMemoryLocker

NewInMemoryLocker creates a new in-process Locker.

Returns

core/lock/memory.go:40-44
func NewInMemoryLocker() *InMemoryLocker

{
	return &InMemoryLocker{
		locks: make(map[string]*lockState),
	}
}