void mb__system_property_read_callback(const prop_info* pi,
                                       void (*callback)(void* cookie,
                                                        const char* name,
                                                        const char* value,
                                                        uint32_t serial),
                                       void* cookie) {
#if MB_ENABLE_COMPAT_PROPERTIES
  // TODO (dimitry): do we need compat mode for this function?
  if (__predict_false(compat_mode)) {
    uint32_t serial = mb__system_property_serial_compat(pi);
    char name_buf[PROP_NAME_MAX];
    char value_buf[PROP_VALUE_MAX];
    mb__system_property_read_compat(pi, name_buf, value_buf);
    callback(cookie, name_buf, value_buf, serial);
    return;
  }
#endif

  while (true) {
    uint32_t serial = mb__system_property_serial(pi);  // acquire semantics
    size_t len = SERIAL_VALUE_LEN(serial);
    char value_buf[len + 1];

    memcpy(value_buf, pi->value, len);
    value_buf[len] = '\0';

    // TODO: see todo in __system_property_read function
    atomic_thread_fence(memory_order_acquire);
    if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
      callback(cookie, pi->name, value_buf, serial);
      return;
    }
  }
}
int mb__system_property_read(const prop_info *pi, char *name, char *value)
{
    if (__predict_false(compat_mode)) {
        return mb__system_property_read_compat(pi, name, value);
    }

    while (true) {
        uint32_t serial = mb__system_property_serial(pi); // acquire semantics
        size_t len = SERIAL_VALUE_LEN(serial);
        memcpy(value, pi->value, len + 1);
        // TODO: Fix the synchronization scheme here.
        // There is no fully supported way to implement this kind
        // of synchronization in C++11, since the memcpy races with
        // updates to pi, and the data being accessed is not atomic.
        // The following fence is unintuitive, but would be the
        // correct one if memcpy used memory_order_relaxed atomic accesses.
        // In practice it seems unlikely that the generated code would
        // would be any different, so this should be OK.
        atomic_thread_fence(memory_order_acquire);
        if (serial ==
                load_const_atomic(&(pi->serial), memory_order_relaxed)) {
            if (name != 0) {
                strcpy(name, pi->name);
            }
            return len;
        }
    }
}
int mb__system_property_read(const prop_info* pi, char* name, char* value) {
#if MB_ENABLE_COMPAT_PROPERTIES
  if (__predict_false(compat_mode)) {
    return mb__system_property_read_compat(pi, name, value);
  }
#endif

  while (true) {
    uint32_t serial = mb__system_property_serial(pi);  // acquire semantics
    size_t len = SERIAL_VALUE_LEN(serial);
    memcpy(value, pi->value, len + 1);
    // TODO: Fix the synchronization scheme here.
    // There is no fully supported way to implement this kind
    // of synchronization in C++11, since the memcpy races with
    // updates to pi, and the data being accessed is not atomic.
    // The following fence is unintuitive, but would be the
    // correct one if memcpy used memory_order_relaxed atomic accesses.
    // In practice it seems unlikely that the generated code would
    // would be any different, so this should be OK.
    atomic_thread_fence(memory_order_acquire);
    if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
      if (name != nullptr) {
        size_t namelen = strlcpy(name, pi->name, PROP_NAME_MAX);
        if (namelen >= PROP_NAME_MAX) {
          LOGE("The property name length for \"%s\" is >= %d;"
               " please use __system_property_read_callback"
               " to read this property. (the name is truncated to \"%s\")",
               pi->name, PROP_NAME_MAX - 1, name);
        }
      }
      return len;
    }
  }
}