go icon indicating copy to clipboard operation
go copied to clipboard

proposal: sync: add Mutex.Do

Open dsnet opened this issue 1 year ago • 15 comments

I propose the following helper method being added to Mutex:

// Do runs f under the protection of the mutex.
func (m *Mutex) Do(f func()) {
    m.Lock()
    defer m.Unlock()
    f()
}

Rationale

I was investigating a sluggish program and the result was because defer m.Unlock() is function scoped.

Consider the following snippet:

func (s *Server) doSomething() {
    s.mu.Lock()
    defer s.mu.Unlock()

    ... // access some critical resource protected by mu

    ... // hundreds of lines of logic that doesn't need protection by mu
}

Initially when doSomething was written it was concise and short such that s.mu.Lock() and the corresponding defer s.mu.Unlock() concisely protected the body of the function. However, as usually goes with software engineering, this function grew in complexity such that logic was added that doesn't care about the resource protected by s.mu. We are now unnecessarily holding the mutex for much longer than necessary (since defer s.mu.Unlock() doesn't run until the function returns). This problem gets worse over time as the unrelated logic grows since the s.mu operations get push father away (in terms of code locality) hiding it's existence and the runtime complexity of the unrelated logic grows as well.

With a Do method, the scope of the critical region becomes clear:

func (s *Server) doSomething() {
    s.mu.Do(func() {
        ... // access some critical resource protected by mu
    })

    ... // hundreds of lines of logic that doesn't need protection by mu
}

dsnet avatar Nov 03 '23 17:11 dsnet