2025-09-25 21:12:43 -04:00
2025-09-26 08:35:21 +07:00
2025-09-25 21:12:43 -04:00
2025-09-26 08:35:21 +07:00
2025-09-26 08:35:21 +07:00

custom-rwmutex

Overview

CustomRwMutex is a lightweight, type-safe wrapper around Gos standard sync.RWMutex that provides safer, more ergonomic access patterns through handler functions. Instead of manually calling Lock()/RLock() and Unlock()/RUnlock(), you pass a function to be executed under the appropriate lock, ensuring proper resource management and reducing the risk of deadlocks or unlocked access.

This pattern enforces scope-bound locking, similar to a defer-based RAII pattern, making concurrent code more readable and less error-prone.

Features

  • Thread-safe: Built on sync.RWMutex, guarantees safe concurrent access.
  • Handler-based API: Prevents manual lock/unlock mistakes.
  • Error propagation: Functions passed in can return errors; theyre propagated up cleanly.
  • No external dependencies: Pure Go, zero dependencies.
  • Minimal overhead: Just a thin wrapper around standard library primitives.

Real-World Example: Shared Config

type Config struct {
	Host string
	Port int
}

func main() {
	config := &Config{Host: "localhost", Port: 8080}
	mu := custom_rwmutex.NewCustomRwMutex()

	// Update config (writer)
	err := mu.WriteHandler(func() error {
		config.Host = "api.example.com"
		config.Port = 443
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}

	// Read config (reader)
	err = mu.ReadHandler(func() error {
		fmt.Printf("Current config: %s:%d\n", config.Host, config.Port)
		return nil
	})
	if err != nil {
		log.Fatal(err)
	}
}

Note

: Since config is captured in closures, ensure its not modified outside the lock context. For full safety, consider returning copies of data from ReadHandler.

Why Use CustomRwMutex?

Problem with sync.RWMutex How CustomRwMutex Solves It
Manual Lock()/Unlock() can be forgotten or mismatched Automatically handled via defer
Risk of leaving locks held due to early returns defer guarantees unlock even on error
No built-in error handling for operations Functions return errors; easily propagated
Verbose and error-prone patterns Clean, functional-style API

Best Practices

  • Keep handlers short and fast — long operations under write locks block all readers and writers.
  • Avoid blocking calls like time.Sleep() or HTTP requests in locked sections.
  • Return errors from handlers — theyre preserved and returned from ReadHandler/WriteHandler.
  • Do not hold references to external data outside the handler unless you are certain they are thread-safe or copied.
  • For heavy read workloads, consider returning a copy of the data instead of exposing references:
var config Data
err := mu.ReadHandler(func() error {
	result := config // copy the data
	// do work on result
	return nil
})
Description
No description provided
Readme Apache-2.0 30 KiB
Languages
Go 100%