// // Copyright 2026 Aarav Ravindra Kharade // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import CSystem // ── Resource Controller ─────────────────────────────────────────── /// Manages CPU and memory resources for the ArkOS runtime. /// /// CPU gating: All background work is dispatched through a concurrent /// queue to prevent runaway thread creation. The queue width matches /// the number of available CPU cores detected at startup. /// /// Memory monitoring: Reads the process RSS from getrusage(2) and /// compares against the configured cap (default 2 GB). public struct ResourceController { /// Execute a unit of work on a new detached pthread. public static func executeOnCorePool(_ work: @escaping () -> Void) { // Box the closure into a heap-allocated context let ctx = UnsafeMutablePointer<(() -> Void)>.allocate(capacity: 1) ctx.initialize(to: work) var thread: pthread_t? let result = pthread_create(&thread, nil, { arg -> UnsafeMutableRawPointer? in let fn = arg!.assumingMemoryBound(to: (() -> Void).self) fn.pointee() fn.deinitialize(count: 1) fn.deallocate() return nil }, ctx) if result == 0 { if let t = thread { pthread_detach(t) } } else { print("ERROR: pthread_create failed with \(result)") ctx.deinitialize(count: 1) ctx.deallocate() } } /// Returns the current process peak RSS in bytes. /// On Linux, getrusage(2) reports ru_maxrss in kilobytes. public static func checkMemoryUsage() -> Int64 { #if canImport(Glibc) || canImport(CSystem) var usage = rusage() if getrusage(RUSAGE_SELF, &usage) == 0 { return Int64(usage.ru_maxrss) * 1024 } #endif return 0 } /// Returns the number of online CPU cores by reading /proc/cpuinfo. public static func getCPUCoreCount() -> Int { #if canImport(Glibc) || canImport(CSystem) let count = sysconf(Int32(_SC_NPROCESSORS_ONLN)) if count > 0 { return count } #endif return 1 } /// Returns total system RAM in bytes by reading /proc/meminfo. public static func getTotalMemory() -> Int64 { if let data = readFileContents("/proc/meminfo") { for line in data.split(separator: "\n") { if line.hasPrefix("MemTotal:") { let parts = line.split(separator: " ").compactMap { Int64($0) } if let kb = parts.first { return kb * 1024 } } } } return 0 } /// Returns available system RAM in bytes by reading /proc/meminfo. public static func getAvailableMemory() -> Int64 { if let data = readFileContents("/proc/meminfo") { for line in data.split(separator: "\n") { if line.hasPrefix("MemAvailable:") { let parts = line.split(separator: " ").compactMap { Int64($0) } if let kb = parts.first { return kb * 1024 } } } } return 0 } /// Returns used memory in bytes (total - available). public static func getUsedMemory() -> Int64 { let total = getTotalMemory() let available = getAvailableMemory() return max(0, total - available) } /// Returns the CPU usage as a percentage (0-100). /// Reads /proc/stat twice with a 100ms interval and computes the delta. public static func getCPUUsage() -> Double { func readCPUStat() -> (idle: Int64, total: Int64)? { guard let data = readFileContents("/proc/stat") else { return nil } let lines = data.split(separator: "\n") guard let cpuLine = lines.first(where: { $0.hasPrefix("cpu ") }) else { return nil } let fields = cpuLine.split(separator: " ").dropFirst().compactMap { Int64($0) } guard fields.count >= 4 else { return nil } let idle = fields[3] + (fields.count > 4 ? fields[4] : 0) // idle + iowait let total = fields.reduce(0, +) return (idle, total) } guard let first = readCPUStat() else { return 0.0 } usleep(100_000) // 100ms guard let second = readCPUStat() else { return 0.0 } let deltaIdle = second.idle - first.idle let deltaTotal = second.total - first.total guard deltaTotal > 0 else { return 0.0 } return Double(deltaTotal - deltaIdle) / Double(deltaTotal) * 100.0 } }