Skip to content

Commit 0bd16ac

Browse files
kpamnanybunjj
andauthored
add parallel AOT compilation throttling with named semaphores (#249)
Co-authored-by: Irfan Bunjaku <[email protected]>
1 parent fbe4d97 commit 0bd16ac

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

src/aotcompile.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@
5656
#include <llvm/Support/FormatAdapters.h>
5757
#include <llvm/Linker/Linker.h>
5858

59+
// Include necessary headers for semaphores
60+
#if defined(_OS_LINUX_)
61+
#include <semaphore.h>
62+
#include <fcntl.h>
63+
#endif
5964

6065
using namespace llvm;
6166

@@ -1645,6 +1650,47 @@ void jl_dump_native_impl(void *native_code,
16451650
has_veccall = !!dataM.getModuleFlag("julia.mv.veccall");
16461651
});
16471652

1653+
#if defined(_OS_LINUX_)
1654+
// This flag tracks if the lock was actually acquired to avoid freeing the semaphore
1655+
// if it was never opened.
1656+
bool lock_acquired = false;
1657+
1658+
auto cleanup = [&](sem_t* s) {
1659+
if (s != SEM_FAILED) {
1660+
if (lock_acquired) {
1661+
// Only post if the lock was successfully acquired.
1662+
sem_post(s);
1663+
}
1664+
sem_close(s);
1665+
}
1666+
};
1667+
1668+
sem_t *par_precomp_sem = SEM_FAILED;
1669+
const char *sem_name = getenv("JL_AOT_PRECOMPILE_SEMAPHORE");
1670+
if (sem_name != NULL) {
1671+
// The count for the semaphore will be initialized by the parent process that spawns compilation sub-processes.
1672+
// This is to ensure that only already created semaphores are used and that we don't end up in a situation
1673+
// where every spawned compilation subprocess creates its own semaphore. Cleanup is handled by the parent process.
1674+
par_precomp_sem = sem_open(sem_name, 0);
1675+
std::unique_ptr<sem_t, decltype(cleanup)> sem_guard(par_precomp_sem, cleanup);
1676+
if (par_precomp_sem == SEM_FAILED) {
1677+
jl_errorf("Failed to open parallel precompilation semaphore '%s': %s", sem_name, strerror(errno));
1678+
} else {
1679+
// Wait for the semaphore, retrying if interrupted by a signal (EINTR).
1680+
int ret;
1681+
do {
1682+
ret = sem_wait(par_precomp_sem);
1683+
} while (ret == -1 && errno == EINTR);
1684+
1685+
if (ret == 0) {
1686+
lock_acquired = true;
1687+
} else {
1688+
jl_errorf("Failed to wait on semaphore '%s': %s", sem_name, strerror(errno));
1689+
}
1690+
}
1691+
}
1692+
#endif
1693+
16481694
{
16491695
// Don't use withModuleDo here since we delete the TSM midway through
16501696
auto TSCtx = data->M.getContext();

0 commit comments

Comments
 (0)