Hi,
[This one is probably too subtle to sort out ahead of time. But, maybe some insights can be gleaned?]I'm trying to sort out the best semantics for (file) locking operations.
I don't have a traditional filesystem. Or, in fact, *any* sort of "filesystem". Rather, I have "objects" (bad choice of words) and they are referenced via "namespaces". A namespace can
*resemble* what most folks would consider as a filesystem hierarchy (e.g., /devices/UART0; /log/startup; /net/connection/27; etc.). So, for example, the "UART0" object may reside in the "devices" container, etc.Each "process" has its own namespace.
Unlike traditional filesystems, my namespaces are not portions of -- not windows into -- a single, unified filesystem (as is typically the case in most conventional systems).
With these things in mind, a single "physical" object (for example, that UART alluded to, above) may be known as: /devices/UART0 to process1 /my/favorite/bitbanger to process2 /output to process3 /log *and* /connections/upstream to process4 etc. Like having multiple hard/softlinks to the same "file", each in a separate *jail*!
Note that /output - and it's binding to that "object" -- only makes sense in the context of (e.g.) process3. Process2 may not have a "/output" in its namespace. Or, may have a /output that is bound to a display, etc.
However, a process can *share* a namespace (or portion thereof) with another process. In which case, "/output" *is* the same "/output" in that "other" process (call it process99). Said another way, if /output was renamed by process3, that other process (process99) would *see* this new name -- /output would disappear (or, be available for reuse by either or both of those processes -- keeping in mind that process2 could still be using it for something entirely different).
With that as background...
Imagine process3 wants to take an exclusive lock on /output (or, any other object that is accessible via its namespace).
I think we would agree that this should prevent process1 from accessing that "physical" object -- the UART, in this example -- for as long as the lock is held. Whether process1's operations block waiting on the resource, return an immediate error or register a callback is immaterial. The point is, the lock applies to the *object*, not the
*name(s)*!But, what (should) happen when process99 tries to rename the object (in the namespace that it shares with process3)? The *name* binding isn't locked, just the object itself!
[Think about this... if you locked the name bindings, then, conceivably, process1 shouldn't be allowed to rename /devices/UART0 as /devices/MyUART while the lock is held!]However, process3 may be in the process of building a namespace for a new process that it is about to spawn -- hence the reason he took the lock on the object... to prevent the object itself from changing from it's current state due to actions by "others". I.e., process3 wants to build a new namespace and reference that UART ("/output") *in* that namespace -- perhaps by the exact same *name*.
How does process3 "lock" the name binding to that object -- at least for the duration of this atomic operation it is attempting?
The workaround is to create another *private* name for the object (using the "handle" that it currently holds) and pass *that* binding on to the new namespace.
The other, heavy-handed approach is to take a lock on the "container" (in this case, "/") so that no changes to the container can be made for this duration.
Namespace operations aren't particularly expensive. OTOH, nor are they *cheap*! I'm mainly concerned with reducing the possibility of latent bugs where subtle races could potentially exist.
Imagine a *traditional* filesystem (single, unified namespace). You have a process that has been accessing a particular "file". It wants to spawn another process to do some further work on that file. So, it wants to pass "filename" to that new process.
But, at the same time, some *other* process (unbeknownst to you) wants to rename "filename" to "myfile" -- because it is ignorant of *your* reference to that "name". Depending on the order in which these competing actions interact, the new process that you spawn may find "filename" is "not found".
As I said, I think this is probably too subtle for a simple resolution. I imagine I'll have to implement it one way or the other... and see how clumsy the approach ends up -- then rework it. :<