1

I am trying to adapt C code, measuring pre/after main time, so it could be accessed from Swift with SC on. But nothing can silent compiler warning, indicating this code is not safe. I've tried every combination of @MainActor/nonisolated(unsafe)/@unchecked Sendable. Any solution/workaround? I am open to consider other solutions to measure pre/after main

Swift code

import Foundation

@MainActor
final class ApplicationLaunchTimeWrapper {
    static let shared = ApplicationLaunchTimeWrapper()

    private var pointer: UnsafeMutablePointer<ApplicationLaunchTimeType> {
        //Reference to var 'ApplicationLaunchTime' is not concurrency-safe because it involves shared mutable state
        UnsafeMutablePointer<ApplicationLaunchTimeType>(mutating: &ApplicationLaunchTime)
    }

    var preMainLaunchTime: CFTimeInterval {
        pointer.pointee.preMainLaunchTime
    }

    var afterMainLaunchTime: CFTimeInterval {
        pointer.pointee.afterMainLaunchTime
    }

    func measurePreMainLaunchTime() {
        pointer.pointee.measurePreMainLaunchTime?()
    }

    func measureAfterMainLaunchTime() {
        pointer.pointee.measureAfterMainLaunchTime?()
    }
}

C/objc code

#ifndef ApplicationLaunchTime_h
#define ApplicationLaunchTime_h

#import <Foundation/Foundation.h>
@import ApplicantServices;

struct ApplicationLaunchTimeType {
    CFTimeInterval preMainLaunchTime;
    CFTimeInterval afterMainLaunchTime;
    CFTimeInterval timeAtMain;
    void (*measurePreMainLaunchTime)(void);
    void (*measureAfterMainLaunchTime)(void);
};

extern struct ApplicationLaunchTimeType ApplicationLaunchTime[1];

#endif /* ApplicationLaunchTime_h */

#import <Foundation/Foundation.h>
#import <sys/sysctl.h>
#import "ApplicationLaunchTime.h"

static CFTimeInterval timeIntervalFromTimeval(const struct timeval time) {
    return time.tv_sec + time.tv_usec / 1e6;
}

static CFTimeInterval processStartTime(void) {
    const size_t namesLength = 4;
    int names[namesLength] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
    struct kinfo_proc processInfo;
    size_t processInfoLength = sizeof(processInfo);
    sysctl(names, namesLength, &processInfo, &processInfoLength, NULL, 0);

    struct timeval processStartTime = processInfo.kp_proc.p_un.__p_starttime;
    return timeIntervalFromTimeval(processStartTime);
}

static void measurePreMainLaunchTime(void) {
    struct timeval currentTime;
    gettimeofday(&currentTime, NULL);

    ApplicationLaunchTime->timeAtMain = timeIntervalFromTimeval(currentTime);
    ApplicationLaunchTime->preMainLaunchTime = ApplicationLaunchTime->timeAtMain - processStartTime();
}

static void measureAfterMainLaunchTime(void) {
    struct timeval currentTime;
    gettimeofday(&currentTime, NULL);
    CFTimeInterval currentTimeInterval = timeIntervalFromTimeval(currentTime);

    ApplicationLaunchTime->afterMainLaunchTime = currentTimeInterval - ApplicationLaunchTime->timeAtMain;
}

struct ApplicationLaunchTimeType ApplicationLaunchTime[1] = {
    {
        .preMainLaunchTime = -DBL_MAX,
        .afterMainLaunchTime = -DBL_MAX,
        .timeAtMain = -DBL_MAX,
        .measurePreMainLaunchTime = measurePreMainLaunchTime,
        .measureAfterMainLaunchTime = measureAfterMainLaunchTime
    }
};
3
  • What's all that #import and @import in the C code? It's certainly not standard C. It looks like you might have meant the #import to be #include, but I have no idea what to make of @import ApplicantServices. Or is that not supposed to be C any longer (making the "C code" heading misleading)? Commented Sep 24 at 21:42
  • My bad, a bit misleading, it is combination of c/objc code, but in this context I believe it is not important. Commented Sep 24 at 21:59
  • Question about accessing extern in Swift with SC Commented Sep 24 at 22:01

1 Answer 1

3

It seems like you are only ever going to access ApplicationLaunchTime from the main actor. You can mark the C global as @MainActor too, using NS_SWIFT_UI_ACTOR.

NS_SWIFT_UI_ACTOR
extern struct ApplicationLaunchTimeType ApplicationLaunchTime[1];

Of course, this doesn't protect anything behind an actor on the C side. It only convinces Swift that this is MainActor isolated. If you have other C code accessing this from other threads, then as the compiler says, this is indeed unsafe.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.