From fe39a007d862ccea80789ff6e1e50d065b4a59ab Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 9 Feb 2026 14:27:22 +0100 Subject: [PATCH] Allow percentages for callbackParallelThreads count --- documentation/new-notes/PR-788.md | 13 ++++++++++ modules/database/src/ioc/db/callback.h | 5 +++- modules/database/src/ioc/db/dbIocRegister.c | 27 +++++++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/documentation/new-notes/PR-788.md b/documentation/new-notes/PR-788.md index 9dc41588c..d5b83c296 100644 --- a/documentation/new-notes/PR-788.md +++ b/documentation/new-notes/PR-788.md @@ -6,3 +6,16 @@ That fixes the problem that an IOC which was started with a limited affinity mask (e.g. using `taskset`) and that called `callbackParallelThreads` with an argument of 0 in the startup script, had started more parallel callback threads than it has CPU cores available. + +The iocsh function `callbackParallelThreads` now also accepts positive or +negative percentages for `count`, referring to the availabple number of cores +and always rounding the number of used cores down with a minimum of 1. + +#### Examples + +```text +# use up to 25% of available cpu cores for callback threads +callbackParallelThreads 25% +# keep at least 10% of available cpu cores unused for callback threads +callbackParallelThreads -10% +``` diff --git a/modules/database/src/ioc/db/callback.h b/modules/database/src/ioc/db/callback.h index 53595c5a9..54f6a8a32 100644 --- a/modules/database/src/ioc/db/callback.h +++ b/modules/database/src/ioc/db/callback.h @@ -199,7 +199,10 @@ DBCORE_API void callbackQueueShow(const int reset); * Calling with count<0 computes the count based on the number of CPU cores * currently available to the calling thread, * eg. Passing -2 on an 8 core system will start 6 worker threads. - * In all cases, at least one worker thread will always run. + * The iocsh wrapper also accepts positive or negative percentages for count, + * which allows to refer to a fraction of the currently available CPU cores, + * always rounding the number of created threads down. + * In any case, at least one worker thread will always run. * * An empty prio name or the special value "*" will modify all priorities. * Otherwise, only the named priority is modified. diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index abdc28fa7..a6194cbac 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -10,6 +10,8 @@ #define EPICS_PRIVATE_API +#include + #include "iocsh.h" #include "callback.h" @@ -27,6 +29,9 @@ #include "dbState.h" #include "db_test.h" #include "dbTest.h" +#include "epicsStdlib.h" +#include "epicsStdio.h" +#include "errlog.h" DBCORE_API extern int callbackParallelThreadsDefault; @@ -503,7 +508,7 @@ static void callbackQueueShowCallFunc(const iocshArgBuf *args) } /* callbackParallelThreads */ -static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt}; +static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgString}; static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString}; static const iocshArg * const callbackParallelThreadsArgs[2] = {&callbackParallelThreadsArg0,&callbackParallelThreadsArg1}; @@ -513,7 +518,25 @@ static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThr "or one of LOW, MEDIUM, or HIGH.\n"}; static void callbackParallelThreadsCallFunc(const iocshArgBuf *args) { - iocshSetError(callbackParallelThreads(args[0].ival, args[1].sval)); + epicsInt32 num = 0; + if (args[0].sval) { + char* end; + long status = epicsParseInt32(args[0].sval, &num, 10, &end); + if (status || (end[0] && strcmp(end, "%") != 0)) + { + fprintf(epicsGetStderr(), + ANSI_RED("Invalid integer '%s'.\n"), args[0].sval); + iocshSetError(-1); + return; + } + if (end[0] == '%') { + /* round down, min 1 */ + if (num < 0) num += 100; + if (num > 0) num = num * epicsThreadGetCPUs() / 100; + if (num <= 0) num = 1; + } + } + iocshSetError(callbackParallelThreads(num, args[1].sval)); } /* dbStateCreate */