Skip to content

Commit 505533a

Browse files
committed
Add RAII wrappers for CF/IO types and update menu text
- Add CFRef<T> template for CoreFoundation types (CFRelease) - Add IORef wrapper for IOKit object types (IOObjectRelease) - Refactor GPU code to use RAII wrappers for all local CF/IO objects - Add 'apple' to shown_gpus description in btop_menu.cpp
1 parent a5179a9 commit 505533a

2 files changed

Lines changed: 77 additions & 54 deletions

File tree

src/btop_menu.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,8 @@ namespace Menu {
598598
"Manually set which gpu vendors to show.",
599599
"",
600600
"Available values are",
601-
"\"nvidia\", \"amd\", and \"intel\".",
601+
"\"nvidia\", \"amd\", \"intel\",",
602+
"and \"apple\".",
602603
"Separate values with whitespace.",
603604
"",
604605
"A restart is required to apply changes."},

src/osx/btop_collect.cpp

Lines changed: 75 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,43 @@ namespace fs = std::filesystem;
121121
namespace rng = std::ranges;
122122
using namespace Tools;
123123

124+
//? RAII wrapper for CoreFoundation types — releases via CFRelease() on destruction
125+
template <typename T>
126+
struct CFRef {
127+
T ref;
128+
CFRef() : ref(nullptr) {}
129+
CFRef(T ref) : ref(ref) {}
130+
~CFRef() { if (ref) CFRelease((CFTypeRef)ref); }
131+
CFRef(const CFRef&) = delete;
132+
CFRef& operator=(const CFRef&) = delete;
133+
CFRef(CFRef&& other) noexcept : ref(other.ref) { other.ref = nullptr; }
134+
CFRef& operator=(CFRef&& other) noexcept {
135+
if (this != &other) { reset(); ref = other.ref; other.ref = nullptr; }
136+
return *this;
137+
}
138+
operator T() const { return ref; }
139+
T get() const { return ref; }
140+
T* ptr() { return &ref; }
141+
void reset(T new_ref = nullptr) {
142+
if (ref) CFRelease((CFTypeRef)ref);
143+
ref = new_ref;
144+
}
145+
T release() { T r = ref; ref = nullptr; return r; }
146+
};
147+
148+
//? RAII wrapper for IOKit object types — releases via IOObjectRelease() on destruction
149+
struct IORef {
150+
io_object_t ref;
151+
IORef() : ref(0) {}
152+
IORef(io_object_t ref) : ref(ref) {}
153+
~IORef() { if (ref) IOObjectRelease(ref); }
154+
IORef(const IORef&) = delete;
155+
IORef& operator=(const IORef&) = delete;
156+
operator io_object_t() const { return ref; }
157+
io_object_t get() const { return ref; }
158+
io_object_t* ptr() { return &ref; }
159+
};
160+
124161
//? --------------------------------------------------- FUNCTIONS -----------------------------------------------------
125162

126163
namespace Cpu {
@@ -208,17 +245,21 @@ namespace Gpu {
208245

209246
//? Read GPU DVFS frequency table from IORegistry pmgr node
210247
static void get_gpu_freqs_from_pmgr() {
211-
io_iterator_t iter;
248+
io_iterator_t iter_raw;
249+
//? matchDict ownership is consumed by IOServiceGetMatchingServices
212250
CFMutableDictionaryRef matchDict = IOServiceMatching("AppleARMIODevice");
213-
if (IOServiceGetMatchingServices(kIOMainPortDefault, matchDict, &iter) != kIOReturnSuccess)
251+
if (IOServiceGetMatchingServices(kIOMainPortDefault, matchDict, &iter_raw) != kIOReturnSuccess)
214252
return;
253+
IORef iter(iter_raw);
215254

216-
io_object_t entry;
217-
while ((entry = IOIteratorNext(iter)) != 0) {
255+
io_object_t entry_raw;
256+
while ((entry_raw = IOIteratorNext(iter)) != 0) {
257+
IORef entry(entry_raw);
218258
char name[128];
219259
if (IORegistryEntryGetName(entry, name) == kIOReturnSuccess and string(name) == "pmgr") {
220-
CFMutableDictionaryRef props = nullptr;
221-
if (IORegistryEntryCreateCFProperties(entry, &props, kCFAllocatorDefault, 0) == kIOReturnSuccess and props) {
260+
CFMutableDictionaryRef props_raw = nullptr;
261+
if (IORegistryEntryCreateCFProperties(entry, &props_raw, kCFAllocatorDefault, 0) == kIOReturnSuccess and props_raw) {
262+
CFRef<CFMutableDictionaryRef> props(props_raw);
222263
CFDataRef dvfs_data = (CFDataRef)CFDictionaryGetValue(props, CFSTR("voltage-states9"));
223264
if (dvfs_data) {
224265
auto len = CFDataGetLength(dvfs_data);
@@ -230,12 +271,9 @@ namespace Gpu {
230271
if (freq > 0) gpu_freqs.push_back(freq / (1000 * 1000)); // Hz -> MHz
231272
}
232273
}
233-
CFRelease(props);
234274
}
235275
}
236-
IOObjectRelease(entry);
237276
}
238-
IOObjectRelease(iter);
239277
}
240278

241279
bool init() {
@@ -245,32 +283,26 @@ namespace Gpu {
245283
get_gpu_freqs_from_pmgr();
246284

247285
//? Set up IOReport channels for GPU Stats and Energy Model
248-
CFStringRef gpu_stats_group = CFStringCreateWithCString(kCFAllocatorDefault, "GPU Stats", kCFStringEncodingUTF8);
249-
CFStringRef gpu_perf_subgroup = CFStringCreateWithCString(kCFAllocatorDefault, "GPU Performance States", kCFStringEncodingUTF8);
250-
CFStringRef energy_group = CFStringCreateWithCString(kCFAllocatorDefault, "Energy Model", kCFStringEncodingUTF8);
251-
252-
CFDictionaryRef gpu_chan = IOReportCopyChannelsInGroup(gpu_stats_group, gpu_perf_subgroup, 0, 0, 0);
253-
CFDictionaryRef energy_chan = IOReportCopyChannelsInGroup(energy_group, nullptr, 0, 0, 0);
286+
CFRef<CFStringRef> gpu_stats_group(CFStringCreateWithCString(kCFAllocatorDefault, "GPU Stats", kCFStringEncodingUTF8));
287+
CFRef<CFStringRef> gpu_perf_subgroup(CFStringCreateWithCString(kCFAllocatorDefault, "GPU Performance States", kCFStringEncodingUTF8));
288+
CFRef<CFStringRef> energy_group(CFStringCreateWithCString(kCFAllocatorDefault, "Energy Model", kCFStringEncodingUTF8));
254289

255-
CFRelease(gpu_stats_group);
256-
CFRelease(gpu_perf_subgroup);
257-
CFRelease(energy_group);
290+
CFRef<CFDictionaryRef> gpu_chan(IOReportCopyChannelsInGroup(gpu_stats_group, gpu_perf_subgroup, 0, 0, 0));
291+
CFRef<CFDictionaryRef> energy_chan(IOReportCopyChannelsInGroup(energy_group, nullptr, 0, 0, 0));
258292

259-
if (not gpu_chan and not energy_chan) {
293+
if (not gpu_chan.get() and not energy_chan.get()) {
260294
Logger::info("Apple Silicon GPU: No IOReport channels found, GPU monitoring unavailable");
261295
return false;
262296
}
263297

264298
//? Merge channels into a single subscription
265-
if (gpu_chan and energy_chan) {
299+
if (gpu_chan.get() and energy_chan.get()) {
266300
IOReportMergeChannels(gpu_chan, energy_chan, nullptr);
267301
}
268-
CFDictionaryRef base_chan = gpu_chan ? gpu_chan : energy_chan;
302+
CFDictionaryRef base_chan = gpu_chan.get() ? gpu_chan.get() : energy_chan.get();
269303

270304
auto size = CFDictionaryGetCount(base_chan);
271305
ior_chan = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, size, base_chan);
272-
if (gpu_chan) CFRelease(gpu_chan);
273-
if (energy_chan and energy_chan != base_chan) CFRelease(energy_chan);
274306

275307
//? Create IOReport subscription
276308
CFMutableDictionaryRef sub_dict = nullptr;
@@ -314,54 +346,45 @@ namespace Gpu {
314346
constexpr int kHIDUsage_TemperatureSensor = 5;
315347
constexpr int64_t kIOHIDEventTypeTemperature = 15;
316348

317-
CFNumberRef nums[2];
318-
CFStringRef keys[2];
319-
keys[0] = CFSTR("PrimaryUsagePage");
320-
keys[1] = CFSTR("PrimaryUsage");
349+
CFStringRef keys[2] = { CFSTR("PrimaryUsagePage"), CFSTR("PrimaryUsage") };
321350
int page = kHIDPage_AppleVendor, usage = kHIDUsage_TemperatureSensor;
322-
nums[0] = CFNumberCreate(nullptr, kCFNumberSInt32Type, &page);
323-
nums[1] = CFNumberCreate(nullptr, kCFNumberSInt32Type, &usage);
324-
CFDictionaryRef match = CFDictionaryCreate(nullptr,
325-
(const void**)keys, (const void**)nums, 2,
326-
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
327-
CFRelease(nums[0]);
328-
CFRelease(nums[1]);
329-
330-
auto system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
331-
if (not system) { CFRelease(match); return -1; }
351+
CFRef<CFNumberRef> num0(CFNumberCreate(nullptr, kCFNumberSInt32Type, &page));
352+
CFRef<CFNumberRef> num1(CFNumberCreate(nullptr, kCFNumberSInt32Type, &usage));
353+
const void* values[] = { num0.get(), num1.get() };
354+
CFRef<CFDictionaryRef> match(CFDictionaryCreate(nullptr,
355+
(const void**)keys, values, 2,
356+
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
357+
358+
CFRef<IOHIDEventSystemClientRef> system(IOHIDEventSystemClientCreate(kCFAllocatorDefault));
359+
if (not system.get()) return -1;
332360
IOHIDEventSystemClientSetMatching(system, match);
333-
CFArrayRef services = IOHIDEventSystemClientCopyServices(system);
334-
CFRelease(match);
361+
CFRef<CFArrayRef> services(IOHIDEventSystemClientCopyServices(system));
335362

336-
if (not services) { CFRelease(system); return -1; }
363+
if (not services.get()) return -1;
337364

338365
double gpu_temp_sum = 0;
339366
int gpu_temp_count = 0;
340367
long count = CFArrayGetCount(services);
341368
for (long i = 0; i < count; i++) {
342369
auto sc = (IOHIDServiceClientRef)CFArrayGetValueAtIndex(services, i);
343370
if (not sc) continue;
344-
CFStringRef name = IOHIDServiceClientCopyProperty(sc, CFSTR("Product"));
345-
if (not name) continue;
371+
CFRef<CFStringRef> name(IOHIDServiceClientCopyProperty(sc, CFSTR("Product")));
372+
if (not name.get()) continue;
346373
char buf[200];
347374
CFStringGetCString(name, buf, 200, kCFStringEncodingASCII);
348375
string n(buf);
349-
CFRelease(name);
350376
//? "GPU MTR Temp Sensor" is the standard Apple Silicon GPU temp sensor name
351377
if (n.find("GPU") != string::npos) {
352-
auto event = IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0);
353-
if (event) {
378+
CFRef<IOHIDEventRef> event(IOHIDServiceClientCopyEvent(sc, kIOHIDEventTypeTemperature, 0, 0));
379+
if (event.get()) {
354380
double temp = IOHIDEventGetFloatValue(event, kIOHIDEventTypeTemperature << 16);
355381
if (temp > 0 and temp < 150) {
356382
gpu_temp_sum += temp;
357383
gpu_temp_count++;
358384
}
359-
CFRelease(event);
360385
}
361386
}
362387
}
363-
CFRelease(services);
364-
CFRelease(system);
365388

366389
if (gpu_temp_count > 0)
367390
return static_cast<long long>(round(gpu_temp_sum / gpu_temp_count));
@@ -416,19 +439,19 @@ namespace Gpu {
416439
uint64_t dt = cur_time - prev_sample_time;
417440
if (dt == 0) dt = 1;
418441

419-
CFDictionaryRef delta = nullptr;
442+
CFRef<CFDictionaryRef> delta;
420443
if (prev_sample) {
421-
delta = IOReportCreateSamplesDelta(prev_sample, cur_sample, nullptr);
444+
delta.reset(IOReportCreateSamplesDelta(prev_sample, cur_sample, nullptr));
422445
CFRelease(prev_sample);
423446
}
424447
prev_sample = cur_sample;
425448
prev_sample_time = cur_time;
426449

427-
if (not delta) return false;
450+
if (not delta.get()) return false;
428451

429452
//? Parse delta samples
430453
CFArrayRef channels = (CFArrayRef)CFDictionaryGetValue(delta, CFSTR("IOReportChannels"));
431-
if (not channels) { CFRelease(delta); return false; }
454+
if (not channels) return false;
432455

433456
long long gpu_utilization = 0;
434457
bool got_gpu_util = false;
@@ -550,7 +573,6 @@ namespace Gpu {
550573
}
551574
}
552575

553-
CFRelease(delta);
554576
return true;
555577
}
556578

0 commit comments

Comments
 (0)