18 #include "alfstream.h"
19 #include "alnumeric.h"
20 #include "aloptional.h"
28 /* Mixing thread piority level */
31 /* Allow reducing the process's RTTime limit for RTKit. */
32 bool AllowRTTimeLimit
{true};
39 const PathNamePair
&GetProcBinary()
41 static al::optional
<PathNamePair
> procbin
;
42 if(procbin
) return *procbin
;
44 auto fullpath
= al::vector
<WCHAR
>(256);
45 DWORD len
{GetModuleFileNameW(nullptr, fullpath
.data(), static_cast<DWORD
>(fullpath
.size()))};
46 while(len
== fullpath
.size())
48 fullpath
.resize(fullpath
.size() << 1);
49 len
= GetModuleFileNameW(nullptr, fullpath
.data(), static_cast<DWORD
>(fullpath
.size()));
53 ERR("Failed to get process name: error %lu\n", GetLastError());
59 if(fullpath
.back() != 0)
60 fullpath
.push_back(0);
62 std::replace(fullpath
.begin(), fullpath
.end(), '/', '\\');
63 auto sep
= std::find(fullpath
.rbegin()+1, fullpath
.rend(), '\\');
64 if(sep
!= fullpath
.rend())
67 procbin
.emplace(wstr_to_utf8(fullpath
.data()), wstr_to_utf8(al::to_address(sep
.base())));
70 procbin
.emplace(std::string
{}, wstr_to_utf8(fullpath
.data()));
72 TRACE("Got binary: %s, %s\n", procbin
->path
.c_str(), procbin
->fname
.c_str());
78 void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
80 std::string pathstr
{path
};
83 TRACE("Searching %s\n", pathstr
.c_str());
85 std::wstring wpath
{utf8_to_wstr(pathstr
.c_str())};
86 WIN32_FIND_DATAW fdata
;
87 HANDLE hdl
{FindFirstFileW(wpath
.c_str(), &fdata
)};
88 if(hdl
== INVALID_HANDLE_VALUE
) return;
90 const auto base
= results
->size();
93 results
->emplace_back();
94 std::string
&str
= results
->back();
97 str
+= wstr_to_utf8(fdata
.cFileName
);
98 } while(FindNextFileW(hdl
, &fdata
));
101 const al::span
<std::string
> newlist
{results
->data()+base
, results
->size()-base
};
102 std::sort(newlist
.begin(), newlist
.end());
103 for(const auto &name
: newlist
)
104 TRACE(" got %s\n", name
.c_str());
109 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
111 auto is_slash
= [](int c
) noexcept
-> int { return (c
== '\\' || c
== '/'); };
113 static std::mutex search_lock
;
114 std::lock_guard
<std::mutex
> _
{search_lock
};
116 /* If the path is absolute, use it directly. */
117 al::vector
<std::string
> results
;
118 if(isalpha(subdir
[0]) && subdir
[1] == ':' && is_slash(subdir
[2]))
120 std::string path
{subdir
};
121 std::replace(path
.begin(), path
.end(), '/', '\\');
122 DirectorySearch(path
.c_str(), ext
, &results
);
125 if(subdir
[0] == '\\' && subdir
[1] == '\\' && subdir
[2] == '?' && subdir
[3] == '\\')
127 DirectorySearch(subdir
, ext
, &results
);
133 /* Search the app-local directory. */
134 if(auto localpath
= al::getenv(L
"ALSOFT_LOCAL_PATH"))
136 path
= wstr_to_utf8(localpath
->c_str());
137 if(is_slash(path
.back()))
140 else if(WCHAR
*cwdbuf
{_wgetcwd(nullptr, 0)})
142 path
= wstr_to_utf8(cwdbuf
);
143 if(is_slash(path
.back()))
149 std::replace(path
.begin(), path
.end(), '/', '\\');
150 DirectorySearch(path
.c_str(), ext
, &results
);
152 /* Search the local and global data dirs. */
153 static const int ids
[2]{ CSIDL_APPDATA
, CSIDL_COMMON_APPDATA
};
156 WCHAR buffer
[MAX_PATH
];
157 if(SHGetSpecialFolderPathW(nullptr, buffer
, id
, FALSE
) == FALSE
)
160 path
= wstr_to_utf8(buffer
);
161 if(!is_slash(path
.back()))
164 std::replace(path
.begin(), path
.end(), '/', '\\');
166 DirectorySearch(path
.c_str(), ext
, &results
);
172 void SetRTPriority(void)
176 if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
))
177 ERR("Failed to set priority level for thread\n");
183 #include <sys/types.h>
187 #include <sys/sysctl.h>
190 #include <FindDirectory.h>
192 #ifdef HAVE_PROC_PIDPATH
195 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
200 #include <sys/time.h>
201 #include <sys/resource.h>
203 #include "dbus_wrap.h"
205 #ifndef RLIMIT_RTTIME
206 #define RLIMIT_RTTIME 15
210 const PathNamePair
&GetProcBinary()
212 static al::optional
<PathNamePair
> procbin
;
213 if(procbin
) return *procbin
;
215 al::vector
<char> pathname
;
218 int mib
[4] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1 };
219 if(sysctl(mib
, 4, nullptr, &pathlen
, nullptr, 0) == -1)
220 WARN("Failed to sysctl kern.proc.pathname: %s\n", strerror(errno
));
223 pathname
.resize(pathlen
+ 1);
224 sysctl(mib
, 4, pathname
.data(), &pathlen
, nullptr, 0);
225 pathname
.resize(pathlen
);
228 #ifdef HAVE_PROC_PIDPATH
231 char procpath
[PROC_PIDPATHINFO_MAXSIZE
]{};
232 const pid_t pid
{getpid()};
233 if(proc_pidpath(pid
, procpath
, sizeof(procpath
)) < 1)
234 ERR("proc_pidpath(%d, ...) failed: %s\n", pid
, strerror(errno
));
236 pathname
.insert(pathname
.end(), procpath
, procpath
+strlen(procpath
));
242 char procpath
[PATH_MAX
];
243 if(find_path(B_APP_IMAGE_SYMBOL
, B_FIND_PATH_IMAGE_PATH
, NULL
, procpath
, sizeof(procpath
)) == B_OK
)
244 pathname
.insert(pathname
.end(), procpath
, procpath
+strlen(procpath
));
250 static const char SelfLinkNames
[][32]{
257 pathname
.resize(256);
259 const char *selfname
{};
261 for(const char *name
: SelfLinkNames
)
264 len
= readlink(selfname
, pathname
.data(), pathname
.size());
265 if(len
>= 0 || errno
!= ENOENT
) break;
268 while(len
> 0 && static_cast<size_t>(len
) == pathname
.size())
270 pathname
.resize(pathname
.size() << 1);
271 len
= readlink(selfname
, pathname
.data(), pathname
.size());
275 WARN("Failed to readlink %s: %s\n", selfname
, strerror(errno
));
279 pathname
.resize(static_cast<size_t>(len
));
282 while(!pathname
.empty() && pathname
.back() == 0)
285 auto sep
= std::find(pathname
.crbegin(), pathname
.crend(), '/');
286 if(sep
!= pathname
.crend())
287 procbin
.emplace(std::string(pathname
.cbegin(), sep
.base()-1),
288 std::string(sep
.base(), pathname
.cend()));
290 procbin
.emplace(std::string
{}, std::string(pathname
.cbegin(), pathname
.cend()));
292 TRACE("Got binary: \"%s\", \"%s\"\n", procbin
->path
.c_str(), procbin
->fname
.c_str());
298 void DirectorySearch(const char *path
, const char *ext
, al::vector
<std::string
> *const results
)
300 TRACE("Searching %s for *%s\n", path
, ext
);
301 DIR *dir
{opendir(path
)};
304 const auto base
= results
->size();
305 const size_t extlen
{strlen(ext
)};
307 while(struct dirent
*dirent
{readdir(dir
)})
309 if(strcmp(dirent
->d_name
, ".") == 0 || strcmp(dirent
->d_name
, "..") == 0)
312 const size_t len
{strlen(dirent
->d_name
)};
313 if(len
<= extlen
) continue;
314 if(al::strcasecmp(dirent
->d_name
+len
-extlen
, ext
) != 0)
317 results
->emplace_back();
318 std::string
&str
= results
->back();
320 if(str
.back() != '/')
322 str
+= dirent
->d_name
;
326 const al::span
<std::string
> newlist
{results
->data()+base
, results
->size()-base
};
327 std::sort(newlist
.begin(), newlist
.end());
328 for(const auto &name
: newlist
)
329 TRACE(" got %s\n", name
.c_str());
334 al::vector
<std::string
> SearchDataFiles(const char *ext
, const char *subdir
)
336 static std::mutex search_lock
;
337 std::lock_guard
<std::mutex
> _
{search_lock
};
339 al::vector
<std::string
> results
;
342 DirectorySearch(subdir
, ext
, &results
);
346 /* Search the app-local directory. */
347 if(auto localpath
= al::getenv("ALSOFT_LOCAL_PATH"))
348 DirectorySearch(localpath
->c_str(), ext
, &results
);
351 al::vector
<char> cwdbuf(256);
352 while(!getcwd(cwdbuf
.data(), cwdbuf
.size()))
359 cwdbuf
.resize(cwdbuf
.size() << 1);
362 DirectorySearch(".", ext
, &results
);
365 DirectorySearch(cwdbuf
.data(), ext
, &results
);
370 // Search local data dir
371 if(auto datapath
= al::getenv("XDG_DATA_HOME"))
373 std::string
&path
= *datapath
;
374 if(path
.back() != '/')
377 DirectorySearch(path
.c_str(), ext
, &results
);
379 else if(auto homepath
= al::getenv("HOME"))
381 std::string
&path
= *homepath
;
382 if(path
.back() == '/')
384 path
+= "/.local/share/";
386 DirectorySearch(path
.c_str(), ext
, &results
);
389 // Search global data dirs
390 std::string datadirs
{al::getenv("XDG_DATA_DIRS").value_or("/usr/local/share/:/usr/share/")};
393 while(curpos
< datadirs
.size())
395 size_t nextpos
{datadirs
.find(':', curpos
)};
397 std::string path
{(nextpos
!= std::string::npos
) ?
398 datadirs
.substr(curpos
, nextpos
++ - curpos
) : datadirs
.substr(curpos
)};
401 if(path
.empty()) continue;
402 if(path
.back() != '/')
406 DirectorySearch(path
.c_str(), ext
, &results
);
409 #ifdef ALSOFT_INSTALL_DATADIR
410 // Search the installation data directory
412 std::string path
{ALSOFT_INSTALL_DATADIR
};
415 if(path
.back() != '/')
418 DirectorySearch(path
.c_str(), ext
, &results
);
428 bool SetRTPriorityPthread(int prio
)
431 #if defined(HAVE_PTHREAD_SETSCHEDPARAM) && !defined(__OpenBSD__)
432 /* Get the min and max priority for SCHED_RR. Limit the max priority to
433 * half, for now, to ensure the thread can't take the highest priority and
436 int rtmin
{sched_get_priority_min(SCHED_RR
)};
437 int rtmax
{sched_get_priority_max(SCHED_RR
)};
438 rtmax
= (rtmax
-rtmin
)/2 + rtmin
;
440 struct sched_param param
{};
441 param
.sched_priority
= clampi(prio
, rtmin
, rtmax
);
442 #ifdef SCHED_RESET_ON_FORK
443 err
= pthread_setschedparam(pthread_self(), SCHED_RR
|SCHED_RESET_ON_FORK
, ¶m
);
446 err
= pthread_setschedparam(pthread_self(), SCHED_RR
, ¶m
);
447 if(err
== 0) return true;
453 WARN("pthread_setschedparam failed: %s (%d)\n", std::strerror(err
), err
);
457 bool SetRTPriorityRTKit(int prio
)
462 WARN("D-Bus not available\n");
466 dbus::ConnectionPtr conn
{dbus_bus_get(DBUS_BUS_SYSTEM
, &error
.get())};
469 WARN("D-Bus connection failed with %s: %s\n", error
->name
, error
->message
);
473 /* Don't stupidly exit if the connection dies while doing this. */
474 dbus_connection_set_exit_on_disconnect(conn
.get(), false);
477 int err
{rtkit_get_min_nice_level(conn
.get(), &nicemin
)};
481 ERR("Could not query RTKit: %s (%d)\n", std::strerror(err
), err
);
484 int rtmax
{rtkit_get_max_realtime_priority(conn
.get())};
485 TRACE("Maximum real-time priority: %d, minimum niceness: %d\n", rtmax
, nicemin
);
487 auto limit_rttime
= [](DBusConnection
*c
) -> int
489 using ulonglong
= unsigned long long;
490 long long maxrttime
{rtkit_get_rttime_usec_max(c
)};
491 if(maxrttime
<= 0) return static_cast<int>(std::abs(maxrttime
));
492 const ulonglong umaxtime
{static_cast<ulonglong
>(maxrttime
)};
494 struct rlimit rlim
{};
495 if(getrlimit(RLIMIT_RTTIME
, &rlim
) != 0)
498 TRACE("RTTime max: %llu (hard: %llu, soft: %llu)\n", umaxtime
,
499 static_cast<ulonglong
>(rlim
.rlim_max
), static_cast<ulonglong
>(rlim
.rlim_cur
));
500 if(rlim
.rlim_max
> umaxtime
)
502 rlim
.rlim_max
= static_cast<rlim_t
>(std::min
<ulonglong
>(umaxtime
,
503 std::numeric_limits
<rlim_t
>::max()));
504 rlim
.rlim_cur
= std::min(rlim
.rlim_cur
, rlim
.rlim_max
);
505 if(setrlimit(RLIMIT_RTTIME
, &rlim
) != 0)
514 err
= limit_rttime(conn
.get());
516 WARN("Failed to set RLIMIT_RTTIME for RTKit: %s (%d)\n",
517 std::strerror(err
), err
);
520 /* Limit the maximum real-time priority to half. */
522 prio
= clampi(prio
, 1, rtmax
);
524 TRACE("Making real-time with priority %d (max: %d)\n", prio
, rtmax
);
525 err
= rtkit_make_realtime(conn
.get(), 0, prio
);
526 if(err
== 0) return true;
529 WARN("Failed to set real-time priority: %s (%d)\n", std::strerror(err
), err
);
531 /* Don't try to set the niceness for non-Linux systems. Standard POSIX has
532 * niceness as a per-process attribute, while the intent here is for the
533 * audio processing thread only to get a priority boost. Currently only
534 * Linux is known to have per-thread niceness.
539 TRACE("Making high priority with niceness %d\n", nicemin
);
540 err
= rtkit_make_high_priority(conn
.get(), 0, nicemin
);
541 if(err
== 0) return true;
544 WARN("Failed to set high priority: %s (%d)\n", std::strerror(err
), err
);
546 #endif /* __linux__ */
551 WARN("D-Bus not supported\n");
563 if(SetRTPriorityPthread(RTPrioLevel
))
565 if(SetRTPriorityRTKit(RTPrioLevel
))