getrusage() user time goes backwards after thread terminates

Originator:foxden
Number:rdar://FB16872543 Date Originated:2025-03-14
Status:Open Resolved:
Product: Product Version:
Classification: Reproducible:
 
The attached tests for iOS and macOS reproduce an issue where the getrusage() API returns a user time which goes backwards if called before and after a thread terminates.

This is also true for the TASK_THREAD_TIMES_INFO API, but that one is documented to only include live threads, so I would expect it to be unstable when threads terminate.

The getrusage() API should always return user and system times which either stay the same or monotonically increase regardless of thread behavior; it should never go backwards.

I verified that the implementation uses MACH_TASK_BASIC_INFO to fetch the user and system time for terminated threads, then uses TASK_THREAD_TIMES_INFO to fetch the user and system time for live threads, and adds the two:

https://github.com/apple-oss-distributions/xnu/blob/8d741a5de7ff4191bf97d57b9f54c2f6d4a15585/bsd/kern/kern_resource.c#L1522-L1569

I reproduced this on iOS 18.4 beta 2 (22E5216d) running in the simulator using Xcode 16.3 beta 2 (16E5121h), as well as on macOS Sequoia 15.3.1.

===

% cat rusage_negative.c 

#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <unistd.h>

static const uint64_t kNsecPerSec = 1000000000;
static const uint64_t kNsecPerUsec = 1000;

struct BusyThreadData {
  pthread_mutex_t mutex;
  pthread_cond_t busyWorkFinishedCondition;
  pthread_cond_t exitCondition;
};

static void ValidateSuccess(int ret, const char *const message) {
  if (ret == 0) {
    return;
  }

  perror(message);
  abort();
}

static uint64_t TimevalToNanos(struct timeval tv) {
  return tv.tv_sec * kNsecPerSec + tv.tv_usec * kNsecPerUsec;
}

static void *BusyThreadMain(void *context) {
  struct BusyThreadData *threadData = (struct BusyThreadData *)context;
  int ret = pthread_mutex_lock(&threadData->mutex);
  ValidateSuccess(ret, "pthread_mutex_lock");

  // Chew up some CPU time by issuing syscalls (to consume system time and user
  // time).
  for (int i = 0; i < 50; i++) {
    (void)getuid();
  }

  ret = pthread_cond_signal(&threadData->busyWorkFinishedCondition);
  ValidateSuccess(ret, "pthread_cond_signal");

  ret = pthread_cond_wait(&threadData->exitCondition, &threadData->mutex);
  ValidateSuccess(ret, "pthread_cond_wait");

  ret = pthread_mutex_unlock(&threadData->mutex);
  ValidateSuccess(ret, "pthread_mutex_unlock");

  return NULL;
}

void RusageBeforeAndAfterSyscalls(struct rusage *before_syscalls,
                                  struct rusage *after_syscalls) {
  int ret = getrusage(RUSAGE_SELF, before_syscalls);
  ValidateSuccess(ret, "getrusage");
  // Chew up some CPU time by issuing syscalls (to consume system time and user
  // time).
  for (int i = 0; i < 50; i++) {
    (void)getuid();
  }

  ret = getrusage(RUSAGE_SELF, after_syscalls);
  ValidateSuccess(ret, "getrusage");
}

void RusageBeforeAndAfterBusyThreadTermination(
    struct rusage *before_thread_termination,
    struct rusage *after_thread_termination) {
  struct BusyThreadData threadData = {
      PTHREAD_MUTEX_INITIALIZER,
      PTHREAD_COND_INITIALIZER,
      PTHREAD_COND_INITIALIZER,
  };

  int ret = pthread_mutex_lock(&threadData.mutex);
  ValidateSuccess(ret, "pthread_mutex_lock");

  // Launch the thread.
  pthread_t busyThread;
  ret = pthread_create(&busyThread, /*attr=*/NULL, BusyThreadMain, &threadData);
  ValidateSuccess(ret, "pthread_create");

  // Release the mutex and go to sleep until the thread finishes consuming CPU
  // time, then aquire the mutex again.
  ret = pthread_cond_wait(&threadData.busyWorkFinishedCondition,
                          &threadData.mutex);
  ValidateSuccess(ret, "pthread_cond_wait");

  // Get the resource usage after the thread consumes CPU time but before it
  // terminates.
  ret = getrusage(RUSAGE_SELF, before_thread_termination);
  ValidateSuccess(ret, "getrusage");

  // Notify the thread to exit.
  ret = pthread_cond_signal(&threadData.exitCondition);
  ValidateSuccess(ret, "pthread_cond_signal");

  // Release the mutex and wait for the thread to terminate.
  ret = pthread_mutex_unlock(&threadData.mutex);
  ValidateSuccess(ret, "pthread_mutex_unlock");
  ret = pthread_join(busyThread, /*value_ptr=*/NULL);
  ValidateSuccess(ret, "pthread_join");

  // Get the resource usage again after the thread terminates.
  ret = getrusage(RUSAGE_SELF, after_thread_termination);
  ValidateSuccess(ret, "getrusage");
}

typedef void (*RusageTestFunction)(struct rusage *before_thread_termination,
                                   struct rusage *after_thread_termination);

// Returns 0 on success, 1 on failure.
int RunTest(RusageTestFunction test_function) {
  struct rusage rusage_before;
  struct rusage rusage_after;
  test_function(&rusage_before, &rusage_after);

  int is_failure = 0;

  uint64_t stime_nanos_before = TimevalToNanos(rusage_before.ru_stime);
  uint64_t stime_nanos_after = TimevalToNanos(rusage_after.ru_stime);

  if (stime_nanos_before > stime_nanos_after) {
    fprintf(stderr,
            "\n\n FAILURE: System time must monotonically increase "
            "(before=%llu, after=%llu)\n",
            stime_nanos_before, stime_nanos_after);
    is_failure = 1;
  }

  uint64_t utime_nanos_before = TimevalToNanos(rusage_before.ru_utime);
  uint64_t utime_nanos_after = TimevalToNanos(rusage_after.ru_utime);

  if (utime_nanos_before > utime_nanos_after) {
    fprintf(stderr,
            "\n\n FAILURE: User time must monotonically increase (before=%llu, "
            "after=%llu)\n",
            utime_nanos_before, utime_nanos_after);
    is_failure = 1;
  }

  return is_failure;
}

static int kDefaultNumTimes = 10000;

int main(int argc, char **argv) {
  int num_times;
  if (argc >= 2) {
    num_times = atoi(argv[1]);
  } else {
    num_times = 10000;
  }
  
  printf("Running getrusage() tests without thread termination...\n\n");
  int num_failures_without_thread_termination = 0;
  for (int i = 0; i < num_times; i++) {
    if (RunTest(&RusageBeforeAndAfterSyscalls) != 0) {
      num_failures_without_thread_termination++;
      printf("!");
    } else {
      printf(".");
    }
  }

  printf("\n\nRunning getrusage() tests with thread termination...\n\n");
  int num_failures_with_thread_termination = 0;
  for (int i = 0; i < num_times; i++) {
    if (RunTest(&RusageBeforeAndAfterBusyThreadTermination) != 0) {
      num_failures_with_thread_termination++;
      printf("!");
    } else {
      printf(".");
    }
  }
  printf("\n\n");
  printf("getrusage() failed %d/%d times without thread termination\n",
         num_failures_without_thread_termination, num_times);
  printf("getrusage() failed %d/%d times with thread termination\n",
         num_failures_with_thread_termination, num_times);

  printf("Done.\n");
  return (num_failures_without_thread_termination == 0 &&
          num_failures_with_thread_termination == 0)
             ? EXIT_SUCCESS
             : EXIT_FAILURE;
}

% clang -Os -o rusage_negative rusage_negative.c && ./rusage_negative 10000
Running getrusage() tests without thread termination...

................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

Running getrusage() tests with thread termination...



 FAILURE: User time must monotonically increase (before=77227000, after=77221000)


 FAILURE: User time must monotonically increase (before=83527000, after=83522000)


 FAILURE: User time must monotonically increase (before=98496000, after=98490000)
......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................!......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................!................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................!.................................................

 FAILURE: User time must monotonically increase (before=99602000, after=99596000)


 FAILURE: User time must monotonically increase (before=110631000, after=110627000)
.................................................................!........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................!.....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

getrusage() failed 0/10000 times without thread termination
getrusage() failed 5/10000 times with thread termination
Done.

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at feedbackassistant.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!