3 ms·
Scalability issue in OP exceptions is because their code is throwing a lot of exceptions from multiple threads at the same time and therefore causing resource c
by menaerus 2y ago
Scalability issue in OP exceptions is because their code is throwing a lot of exceptions from multiple threads at the same time and therefore causing resource contention in libgcc unwind implementation.
Since you have only shown a simple example of throwing exceptions I'm not sure I understood your point how SJLJ exceptions solve the scalability problem? Truth to be told I'm not very familiar with SJLJ.
- jart 2y agoThe SJLJ runtime doesn't lock a global mutex seven times each time you throw an exception. I'm not kidding. If you use DWARF exceptions then, each time you use the throw keyword, libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_lock needs to be acquired six time in read mode, and once in write mode. Yes it's a GIL. I traced the address of the lock objects myself. That effectively makes throwing an exception a serialized operation. Only one exception can be thrown at a time, per process. Not only will this limit your throughput to one core, it'll also saturate all the other cores on your system with pointless busy work too, if your pthread_rwlock_t implementation isn't very good, e.g. glibc, musl. See also https://justine.lol/mutex/ https://justine.lol/mutex/ The example I gave is just the tip of the iceberg. I'm using it to analyze the runtimes beneath.
- fweimer 2y agoSJLJ exceptions do not need to perform ancillary data lookup using program counter values. Instead, there is code on the non-error path that maintains a linked list of exception landing sites when try blocks are entered/exited. Run-time type information is still needed for catch clause matching, but that data can be accessed easily via the vtable pointer and information next to the vtable. None of this needs to access mutable global data structures. DWARF exception handling needs DWARF unwind information, which is mapped in memory along with the code. But the stack only has program counters, not the addresses of the DWARF data. Therefore, the first step is to locate the DWARF data. The semi-portable interface (supported on GNU/Linux, various BSD, and likely others) for that is dl_iterate_phdr, and that invokes a callback under a global lock. The global lock is required because the list of objects can change in response to dlopen and dlclose calls. With the new way, the GCC unwinder directly asks the glibc dynamic linker for the ELF object corresponding to a program counter value and its DWARF unwind information address. This way, the synchronization with dlopen/dlclose is an internal dynamic linker implementation detail.