Skip to content

sync Module

The sync module provides synchronization primitives for concurrent programming, including mutexes and read-write mutexes. These primitives help protect shared data structures from race conditions when using Harneet's do/await concurrency model.

Overview

The sync module offers two main types of synchronization primitives:

  • Mutex: Provides mutual exclusion for protecting shared resources
  • RWMutex: Optimized for scenarios with frequent reads and infrequent writes

All sync operations follow Harneet's error handling patterns, returning (result, error) tuples.

Factory Functions

NewMutex()

Creates a new mutex for mutual exclusion.

Parameters: - None

Returns: - (mutex, error): A new mutex instance and error status

Example:

Create Mutex
package main
import sync, fmt

var mutex, err = sync.NewMutex()
if err != None {
    fmt.Println("Error creating mutex:", err)
    return
}

var success, lockErr = mutex.Lock()
if lockErr != None {
    fmt.Println("Lock failed:", lockErr)
    return
}

// Protected code section
fmt.Println("Critical section")

var unlockSuccess, unlockErr = mutex.Unlock()
if unlockErr != None {
    fmt.Println("Unlock failed:", unlockErr)
}


NewRWMutex()

Creates a new read-write mutex optimized for concurrent reads.

Parameters: - None

Returns: - (rwmutex, error): A new read-write mutex instance and error status

Example:

Create RWMutex
package main
import sync, fmt

var rwmutex, err = sync.NewRWMutex()
if err != None {
    fmt.Println("Error creating rwmutex:", err)
    return
}

// Multiple readers can acquire read locks simultaneously
var readSuccess, readErr = rwmutex.RLock()
if readErr != None {
    fmt.Println("Read lock failed:", readErr)
    return
}

// Read from shared resource
fmt.Println("Reading data")

var readUnlockSuccess, readUnlockErr = rwmutex.RUnlock()
if readUnlockErr != None {
    fmt.Println("Read unlock failed:", readUnlockErr)
}


Mutex Operations

mutex.Lock()

Acquires the mutex lock, blocking until available.

Parameters: - None

Returns: - (bool, error): Success status and error information

Example:

Lock Mutex
1
2
3
4
5
6
var success, err = mutex.Lock()
if err != None {
    fmt.Println("Lock failed:", err)
} else {
    fmt.Println("Lock acquired successfully")
}


mutex.Unlock()

Releases the mutex lock.

Parameters: - None

Returns: - (bool, error): Success status and error information

Example:

Unlock Mutex
1
2
3
4
5
6
var success, err = mutex.Unlock()
if err != None {
    fmt.Println("Unlock failed:", err)
} else {
    fmt.Println("Lock released successfully")
}


mutex.TryLock()

Attempts to acquire the mutex lock without blocking.

Parameters: - None

Returns: - (bool, error): Lock acquisition status and error information

Example:

Try Lock
var acquired, err = mutex.TryLock()
if err != None {
    fmt.Println("TryLock error:", err)
} else if acquired {
    fmt.Println("Lock acquired immediately")
    // Use protected resource
    var _, unlockErr = mutex.Unlock()
} else {
    fmt.Println("Lock not available")
}


RWMutex Operations

rwmutex.RLock()

Acquires a read lock, allowing concurrent readers.

Parameters: - None

Returns: - (bool, error): Success status and error information

Example:

Read Lock
1
2
3
4
5
6
var success, err = rwmutex.RLock()
if err != None {
    fmt.Println("Read lock failed:", err)
} else {
    fmt.Println("Read lock acquired")
}


rwmutex.RUnlock()

Releases a read lock.

Parameters: - None

Returns: - (bool, error): Success status and error information

Example:

Read Unlock
1
2
3
4
5
6
var success, err = rwmutex.RUnlock()
if err != None {
    fmt.Println("Read unlock failed:", err)
} else {
    fmt.Println("Read lock released")
}


rwmutex.Lock()

Acquires an exclusive write lock, blocking all other access.

Parameters: - None

Returns: - (bool, error): Success status and error information

Example:

Write Lock
1
2
3
4
5
6
var success, err = rwmutex.Lock()
if err != None {
    fmt.Println("Write lock failed:", err)
} else {
    fmt.Println("Write lock acquired")
}


rwmutex.Unlock()

Releases the write lock.

Parameters: - None

Returns: - (bool, error): Success status and error information

Example:

Write Unlock
1
2
3
4
5
6
var success, err = rwmutex.Unlock()
if err != None {
    fmt.Println("Write unlock failed:", err)
} else {
    fmt.Println("Write lock released")
}


rwmutex.TryRLock()

Attempts to acquire a read lock without blocking.

Parameters: - None

Returns: - (bool, error): Lock acquisition status and error information

Example:

Try Read Lock
var acquired, err = rwmutex.TryRLock()
if err != None {
    fmt.Println("TryRLock error:", err)
} else if acquired {
    fmt.Println("Read lock acquired immediately")
    // Read from resource
    var _, unlockErr = rwmutex.RUnlock()
} else {
    fmt.Println("Read lock not available")
}


rwmutex.TryLock()

Attempts to acquire a write lock without blocking.

Parameters: - None

Returns: - (bool, error): Lock acquisition status and error information

Example:

Try Write Lock
var acquired, err = rwmutex.TryLock()
if err != None {
    fmt.Println("TryLock error:", err)
} else if acquired {
    fmt.Println("Write lock acquired immediately")
    // Modify resource
    var _, unlockErr = rwmutex.Unlock()
} else {
    fmt.Println("Write lock not available")
}


Integration with Concurrency

The sync module integrates seamlessly with Harneet's do/await concurrency model:

Concurrency Integration
package main
import sync, fmt

var sharedCounter = 0
var counterMutex, _ = sync.NewMutex()

function incrementCounter(name string, times int) string {
    for var i = 0; i < times; i = i + 1 {
        var _, err = counterMutex.Lock()
        if err != None {
            return "lock error: " + err.Error()
        }

        sharedCounter = sharedCounter + 1
        fmt.Printf("%s: counter = %d\n", name, sharedCounter)

        var _, unlockErr = counterMutex.Unlock()
        if unlockErr != None {
            return "unlock error: " + unlockErr.Error()
        }
    }
    return name + " completed"
}

function main() {
    // Use with existing concurrency
    var task1 = do incrementCounter("Task1", 5)
    var task2 = do incrementCounter("Task2", 5)

    var result1 = await(task1)
    var result2 = await(task2)

    fmt.Println("Results:", result1, result2)
    fmt.Println("Final counter:", sharedCounter)
}

Best Practices

Avoiding Deadlocks

  1. Consistent Lock Ordering: Always acquire multiple locks in the same order
  2. Timeout Patterns: Use TryLock operations with retry logic
  3. Minimal Critical Sections: Keep locked sections as short as possible

Performance Considerations

  1. Use RWMutex for Read-Heavy Workloads: When reads significantly outnumber writes
  2. Avoid Lock Contention: Design data structures to minimize shared state
  3. Consider Lock-Free Alternatives: For simple operations like counters

Error Handling

Always check error returns from mutex operations:

Error Handling
1
2
3
4
5
6
7
8
9
var success, err = mutex.Lock()
if err != None {
    // Handle error appropriately
    fmt.Println("Lock failed:", err)
    return
}

// Use blank identifier only when errors can be safely ignored
var _, _ = mutex.Unlock()

Common Patterns

Producer-Consumer

Producer-Consumer Pattern
import sync, fmt

var sharedData = "empty"
var dataMutex, _ = sync.NewMutex()

function producer(name string, data string) {
    var lockSuccess, lockErr = dataMutex.Lock()
    if lockErr == None {
        var formattedData = fmt.Sprintf("%s: %s", name, data)
        sharedData = formattedData
        fmt.Printf("Producer %s stored: %s\n", name, sharedData)
        var unlockSuccess, unlockErr = dataMutex.Unlock()
    }
}

function consumer(name string) string {
    var lockSuccess, lockErr = dataMutex.Lock()
    var result = ""
    if lockErr == None {
        result = sharedData
        fmt.Printf("Consumer %s read: %s\n", name, result)
        var unlockSuccess, unlockErr = dataMutex.Unlock()
    }
    return result
}

// Example usage:
// producer("P1", "important data")
// var data = consumer("C1")

Shared Counter

Shared Counter
import sync

var counter = 0
var counterMutex, _ = sync.NewMutex()

function safeIncrement() int {
    var _, _ = counterMutex.Lock()
    counter = counter + 1
    var result = counter
    var _, _ = counterMutex.Unlock()
    return result
}

function safeGet() int {
    var _, _ = counterMutex.Lock()
    var result = counter
    var _, _ = counterMutex.Unlock()
    return result
}