/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*!
 * Implement a wrapper around pthread_create that sets the thread stack
 * size to a chosen value.
 *
 * TVM runtime uses std::thread, but the C++ standard does not provide
 * any means of controlling thread attributes (like stack size). Because
 * of that, any thread created by the std::thread constructor will use
 * default attributes. The default stack size for a thread in QuRT is 16kB.
 * This has proven to be insufficient in the past, so we need to increase
 * it.
 * When libtvm_runtime.so is linked, a linker flag --wrap=pthread_create
 * is used, which causes the linker to rename all uses of pthread_create
 * with references to __wrap_pthread_create. This file implements the
 * __wrap function to set the larger stack size and call the actual
 * pthread_create. The call to pthread_create here must not be renamed,
 * so this function cannot be included in the TVM runtime binary.
 * Instead, it's implemented in a separate shared library.
 */

#include <pthread.h>

#include "HAP_farf.h"

static constexpr size_t kThreadStackSize = 128 * 1024;  // 128kB

// Make sure the function has C linkage.
extern "C" {
int __wrap_pthread_create(pthread_t* restrict thread, const pthread_attr_t* restrict attr,
                          void* (*start)(void*), void* restrict arg);
}

int __wrap_pthread_create(pthread_t* restrict thread, const pthread_attr_t* restrict attr,
                          void* (*start)(void*), void* restrict arg) {
  pthread_attr_t def_attr;
  if (attr == nullptr) {
    if (int rc = pthread_attr_init(&def_attr)) {
      FARF(ERROR, "pthread_attr_init failed: rc=%08x", rc);
      return rc;
    }
    if (int rc = pthread_attr_setstacksize(&def_attr, kThreadStackSize)) {
      FARF(ERROR, "pthread_attr_setstacksize failed: rc=%08x", rc);
      return rc;
    }
    attr = &def_attr;
  }
  size_t stack_size = 0;
  if (int rc = pthread_attr_getstacksize(attr, &stack_size)) {
    FARF(ERROR, "pthread_attr_setstacksize failed: rc=%08x", rc);
    return rc;
  }
  FARF(ALWAYS, "launching thread with stack_size=%zu", stack_size);
  int t = pthread_create(thread, attr, start, arg);
  if (int rc = pthread_attr_destroy(&def_attr)) {
    FARF(ERROR, "pthread_attr_destroy failed (after pthread_create): rc=%08x", rc);
  }
  return t;
}
