Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ideas wanted: Rely on language features to enforce locking correctness #1389

Open
mtrmac opened this issue Oct 14, 2022 · 2 comments
Open

Ideas wanted: Rely on language features to enforce locking correctness #1389

mtrmac opened this issue Oct 14, 2022 · 2 comments

Comments

@mtrmac
Copy link
Collaborator

mtrmac commented Oct 14, 2022

Looking at #1324, #1332, #1379 , it’s not absolutely required but boy it would be nice if we could have the programming language enforce locking invariants

  • That locks that are supposed to be held are held
  • That reads to struct fields only happen with a read lock held
  • That writes to struct fields only happen with a write lock held.

At the thousands of lines of c/storage code, and about half a dozen locks involved, it’s only natural that some mistakes will happen and we could certainly use some help noticing them.

Sadly, Go is very weak for these purposes. So, this is an open-ended call for ideas.

@mtrmac
Copy link
Collaborator Author

mtrmac commented Oct 14, 2022


  • I have nothing at all for protecting access to struct fields. AFAICS we could make the struct completely private and wrap everything in getters/setters … written manually. Not worth it.
  • For enforcing API invariants:
    • Interfaces sort of work:
      type unlockedStore interface {
         startReading() readableStore
         startWriting() writableStore
      }
      
      type readableStore interface {
         // readers
         Close()
      }
      // and writableStore similarly
      but if the three interfaces are implemented by the same struct, calls within that struct are no longer protected (e.g. a “reader” method can call a “writer” method no the same object)
    • “lock tokens” work:
      type readToken struct {} // unique type, zero cost to pass around
      type writeToken struct {} // unique type, zero cost to pass around
      type store interface { // or just methods on a struct
         startReading() readToken
         stopReading(readToken)
         startWriting() writeToken
         stopWriting(writeToken)
      
         readSomething(readToken)
         writeSomething(writeToken, valueType)
      }
      but require all call sites to carry around an extra token value. That has zero or close-to-zero run-time cost, but the code is larger.

Is there something else that would work? Anything that would be worth doing?

@mtrmac
Copy link
Collaborator Author

mtrmac commented Jan 3, 2023

  • I have nothing at all for protecting access to struct fields

@vrothberg came up with

type stateful struct {
    lock sync.Mutex
    requiresLock struct {
        field …
    }
}

which is much better than nothing. A downside is that such a type can’t be created with a trivial struct initializer: either .requiresLock.field must be initialized after the struct is created, or requiresLock must become an out-of-line separately-defined named type — that’s probably still well worth the cost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant