3 ms·
The refactoring to C_UnderLock is straightforward but the name alone gives away the problem: you have moved the burden to the developer rather than guaranteeing
by alkonaut 4y ago
The refactoring to C_UnderLock is straightforward but the name alone gives away the problem: you have moved the burden to the developer rather than guaranteeing it at runtime. A private method with a screaming comment above it helps somewhat of course, but if there is something I have learned as a developer is that given a couple of decades, any foot will be shot.
lock(something) when recursive will do one or two things: either aquire the lock or simply validate that it’s held.
Is there a different pattern that simply allows validating in C_UnderLock that the lock is in fact held?
so in A and B you do lock(_mutex) but in C you do assert_lock(_mutex) which either panics or continues? What would that look like?
- BazookaMusic 4y agoYou can do it with the type system, although in C# it is a bit clunky. For example let's say you have a class Resource that needs to be locked and a method Foo(Resource) that needs to operate on the resource under a lock. You could change Foo(Resource) to Foo(LockedResource). LockedResource could be a class that is defined using a lock object and a resource and takes the lock or validates that lock is taken in the constructor. Then by making the object disposable the release can be handled as well. This way the type system handles the validation. This is kinda like a budget version of having the type system support locking and concurrency like Rust does.
- Akronymus 4y agoIn f# this would be even easier. (I wrote some capabilities based code before (for example, this part is only allowed read accesses to the DB)) When I tried something similar in c# I almost despaired.
- alkonaut 4y agoDisposable (unlike a Rust borrow) doesn’t give much in terms of guarantees though. You aren’t forced to dispose at method boundaries. But perhaps in C# you could use some locking token and make it one of the types that can’t leave the stack?
- masklinn 4y ago> so in A and B you do lock(_mutex) but in C you do assert_lock(_mutex) which either panics or continues? What would that look like? Make "lock being held" into a resource, which can then be passed down to callees to assert (at compile-time) that a lock is being held. Even better if the lock itself is associated with (and lends on locking) values, then just having access to the protected resource indicates that you hold the lock. Obviously without language support for ownership and borrowing this has limited safety as you can stash a lock token and reuse it later, despite not holding the lock. Then again, even in Rust if you really wanted to fuck someone's day you could probably `transmute` garbage into a lock token.
- gpderetta 4y agoIn C++ this can be done by having functions that need to be called with the lock held, but won't take the lock themselves, take the lock guard as a parameter. There are also patterns like the synchronized wrapper were the lock is bolted on transparently and all the functions in a class can call each other without worrying about the lock.
- archagon 4y ago> so in A and B you do lock(_mutex) but in C you do assert_lock(_mutex) which either panics or continues? What would that look like? What's wrong with doing exactly what you said? If somebody ends up misusing C_UnderLock, their program will crash, and they can go fix their bug.
- alkonaut 4y agoI’m wondering if there is such a primitive like assert_lock() or similar, which will let me do that.
- archagon 4y agoApple has one for `os_unfair_lock`: https://developer.apple.com/documentation/os/2976314-os_unfair_lock_assert_owner https://developer.apple.com/documentation/os/2976314-os_unfa...
- alkonaut 4y agoLooks like if (!Monitor.IsEntered(_x)) throw new InvalidOperation. Will do the trick in C#