Add macros for mutex use around libc functions that need them#24098
Add macros for mutex use around libc functions that need them#24098khwilliamson wants to merge 30 commits intoPerl:bleadfrom
Conversation
|
@tonycoz wrote
|
|
The answer is that it turns out that #error is not valid as an expansion of a #define. So, I got rid of the |
|
@tonycoz wrote:
ok
I don't know what to do about this. First, for the casual reader, these all expand to no-ops except on a threaded build. This commit doesn't have anything about how these locks are actually implemented. My proposed implementation, I believe avoids the possibility of deadlock, and doesn't introduce any new mutexes, which doing would make deadlock possible. These locks look like they have three possible mutexes, the existing ENV and LOCALE ones, plus a GENeric one. That one is mapped to either of the other two, depending on the Configuration. It turns out on systems with thread-safe locales, the LOCALE mutex is essentially unused, so is available for the generic one. Otherwise, that mutex can get used a lot, and so the ENV mutex is used instead, as we expect changes to the environment to be fairly rare. The ENV and LOCALE mutexes have co-existed for quite a few releases now without deadlock reports. Deadlock is avoided by always doing things in a particular order. By using a common mutex for unrelated libc calls, threads block that wouldn't if there were a mutex for just the related libc calls. For example But if we use a common mutex for a series of operations, unlocking it at the end, threads using unrelated operations which use the same mutex are locked out for the duration. It would be better I think to have a new mutex for just the series. This would entail that the code using it be carefully crafted to not have deadlock potential. And, then would there be a mutex for each type of series, like I do have text in the comments indicating there is an issue here. But maybe the solution is to just not generate locks for functions that are part of a series |
3445caf to
b1a36ac
Compare
The problem is that something like: is invalid, since other operations may have occurred in the gaps between locks to invalidate the stored error value. Now the user could just lock everything they need in a block: but that's wasteful, and annoying. Note that the extended span of the lock here is about guarding access to a working API, not just about preventing crashes that might occur if there were multiple concurrent dbm_store()s in progress. |
b1a36ac to
4d0fba5
Compare
|
I removed the locks for functions that need to be used in conjunction with other ones as an atomic unit |
4d0fba5 to
5e6ee19
Compare
regen/lock_definitions.pl
Outdated
| print $l <<EOT; | ||
| /* This file contains macros to wrap their respective libc uses to ensure that | ||
| * those uses are thread-safe in a multi-threaded environment. It is ordered | ||
| * alphabetically by fucntion name. |
I assume (hope?) it will be perl's headers including this. In any case, I think Whether that's |
5e6ee19 to
cbbec68
Compare
|
I have now added commits intended to complete this pull request. Previously, only the file The intent is that an XS writer or perl core writer can wrap calls to libc functions with the appropriate macros without needing to understand the nuances, and get thread-safe operation without the possibility of deadlock One implementation is for when there are thread-safe locales. In this case, an existing mutex is essentially unused, and can be repurposed. The other implementation works, with somewhat less performance, otherwise. The file is not intended to be included directly into XS space, but the definitions are always there to anyone who includes perl.h |
d08ee38 to
1a940d6
Compare
This file is intended to insulate C code from thread issues, when used as directed. This commit merely creates the file; it is not #included yet.
These symbols aren't really documented, and we're not about to. So move them to the resolved list.
This is for debugging mutex locking/unlocking. Convert some locale debug statements that are really about locking to use this. This is not compile in by default because of the overhead during time-critical operations. It requires -Accflags=-DPERL_DEBUG_MUTEXES to be passed to Configure. Its use is for very low level problems. It is incompatible with being compiled to have mem log. Putting that logic in its definition simplifies some code a bit.
This is in preparation for the next commit when it otherwise would be confusing. The terminology is also changed to exclusive lock, as that is clearer.
Make the unlock mate of its corresponding lock adjacent to it
This sets xcounter unconditionally, but now after the lock. It's a small thing, but I think it is slightly clearer
Perl has mutexes to prevent cooperating threads from colliding in accessing shared resources. This requires all threads to use these; a rogue thread that doesn't follow the protocol can wreak havoc. We can make sure that the perl core fully cooperates, but an XS module could go rogue. A thread can declare that it accesses the resource only to query it, and will not change it in any way. Any number of threads can be accessing the resource in this manner at the same time without fear of collisions. We say that such threads have read-only access. A thread can also declare that it will be changing the resource. Only one thread can safely be doing this at a time. It has exclusive access. Perl has macros to call to lock and unlock the resource for both types of access. While a thread has exclusive access, any other thread wanting either type of access will hang until the first thread releases it. Conversely, if a thread wants exclusive access, it will hang until all the threads that have read-only access release their locks. Any number of threads may have simultaneous read-only locks. The macros allow a thread to lock a resource while already holding it. They are called general semaphores or recursive locks. Only when the unlock matching the first lock is executed does the resource get released. Locking the rest of the system out from a resource should be done for the shortest time possible to prevent bottlenecks. A recursive lock could encourage the bad habit of locking it longer than the minimum possible. But these locks have been added to existing code. It would be a tremendous amount of effort to change that code, hence the recursiveness. This commit temporarily removes a few DEBUG_K statements. This is so that the logic of the code can be seen without this distraction. The next commit adds them, and more like them, back in. Commit 262c141 added two unused macros for future use. It turns out this was somewhat premature, because the implementation was buggy (except they're not called). This new commit fixes that, and the next few commits will finally start to use these macros. The problem was the interaction with exclusive locking attempts. If a thread owns an exclusive lock, and then recursively asks for a read one, that should trivially succeed. But instead it would hang, depending on the system's locking implementation. The solution is to add a per-thread bookkeeping counter ourselves, so we bypass the system's in this situation. Conversely, an attempt to add an exclusive lock when already holding a read lock was't handled properly. I discovered via testing that this can lead to deadlock (the comments added in this commit explain the scenario). This commit hence panics when such an attempt is made. The panic is to try to make sure that code that can trigger this doesn't get shipped.
Now that we have a general reentrant read-only lock, use it. This prepares for future commits
A lock by this name has existed for a few releases, but it actually was an exclusive lock. Previous commits have added the infrastructure to do an actual read-only lock. Take advantage of it.
That header file maps libc calls that need mutex locking into various macros, based on the characteristics of each libc call. But it leaves it to perl.h to define those various expansions. This commit does that, based on the Configuration of the build.
This is clearer code.
The many macros in this file now all begin with "PERL_" to avoid namespace pollution. However, a few names already existed in previous Perl releases without that prefix. This commit allows those names to be retained, along with the new spellings.
This commit could break something that relies on these, but it would be broken anyway because these locks are ineffective. The reason is that these functions need to be used in conjunction with other functions in a critical section, and the locks are not designed for that. Using them could give a false sense of security.
The mutex lock macros in this commit are retaine for backwards compatibility, but are now formulated in terms of their new names
This makes it more convenient for a reader to find a line
This code had gotten fragmented. Put as much stuff controlled by the same #ifdef logic under a single #ifdef
Since 5335601, there has been the capability of having nested locks, where the inner one is necessary in all conditions, and the outer one is desirable in just some conditions. But this hasn't been enabled because in some Configurations it could cause a deadlock, with the thread locking the ENV mutex, and then in a nested call trying to lock it again. The previous commit made this mutex reentrant, so the deadlock is gone.
Use these new macros to assure thread safety
1a940d6 to
5d6c2ab
Compare
5d6c2ab to
fe51b79
Compare
aa693a7 to
283c699
Compare
This replaces #22283
This file is intended to insulate C code from thread issues, when used as directed.
This commit merely creates the file; it is not #included yet.