@@ -121,6 +121,43 @@ namespace fs = std::filesystem;
121121namespace rng = std::ranges;
122122using 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
126163namespace 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