text: /*
   * Copyright (c) 2016 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <ztest.h>
  
  #include "tests_thread_apis.h"
  
  static ZTEST_BMEM int execute_flag;
  
  K_SEM_DEFINE(sync_sema, 0, 1);
  #define BLOCK_SIZE 64
  
  static void thread_entry(void *p1, void *p2, void *p3)
  {
  	execute_flag = 1;
  	k_msleep(100);
  	execute_flag = 2;
  }
  
  static void thread_entry_abort(void *p1, void *p2, void *p3)
  {
  	/**TESTPOINT: abort current thread*/
  	execute_flag = 1;
  	k_thread_abort(k_current_get());
  	/*unreachable*/
  	execute_flag = 2;
  	zassert_true(1 == 0, NULL);
  }
  /**
   * @ingroup kernel_thread_tests
   * @brief Validate k_thread_abort() when called by current thread
   *
   * @details Create a user thread and let the thread execute.
   * Then call k_thread_abort() and check if the thread is terminated.
   * Here the main thread is also a user thread.
   *
   * @see k_thread_abort()
   */
  void test_threads_abort_self(void)
  {
  	execute_flag = 0;
  	k_thread_create(&tdata, tstack, STACK_SIZE, thread_entry_abort,
  			NULL, NULL, NULL, 0, K_USER, K_NO_WAIT);
  	k_msleep(100);
  	/**TESTPOINT: spawned thread executed but abort itself*/
  	zassert_true(execute_flag == 1, NULL);
  }
  
  /**
   * @ingroup kernel_thread_tests
   * @brief Validate k_thread_abort() when called by other thread
   *
   * @details Create a user thread and abort the thread before its
   * execution. Create a another user thread and abort the thread
   * after it has started.
   *
   * @see k_thread_abort()
   */
  void test_threads_abort_others(void)
  {
  	execute_flag = 0;
  	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
  				      thread_entry, NULL, NULL, NULL,
  				      0, K_USER, K_NO_WAIT);
  
  	k_thread_abort(tid);
  	k_msleep(100);
  	/**TESTPOINT: check not-started thread is aborted*/
  	zassert_true(execute_flag == 0, NULL);
  
  	tid = k_thread_create(&tdata, tstack, STACK_SIZE,
  			      thread_entry, NULL, NULL, NULL,
  			      0, K_USER, K_NO_WAIT);
  	k_msleep(50);
  	k_thread_abort(tid);
  	/**TESTPOINT: check running thread is aborted*/
  	zassert_true(execute_flag == 1, NULL);
  	k_msleep(1000);
  	zassert_true(execute_flag == 1, NULL);
  }
  
  /**
   * @ingroup kernel_thread_tests
   * @brief Test abort on a terminated thread
   *
   * @see k_thread_abort()
   */
  void test_threads_abort_repeat(void)
  {
  	execute_flag = 0;
  	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
  				      thread_entry, NULL, NULL, NULL,
  				      0, K_USER, K_NO_WAIT);
  
  	k_thread_abort(tid);
  	k_msleep(100);
  	k_thread_abort(tid);
  	k_msleep(100);
  	k_thread_abort(tid);
  	/* If no fault occurred till now. The test case passed. */
  	ztest_test_pass();
  }
  
  bool abort_called;
  void *block;
  
  static void delayed_thread_entry(void *p1, void *p2, void *p3)
  {
  	execute_flag = 1;
  
  	zassert_unreachable("Delayed thread shouldn't be executed");
  }
  
  /**
   * @ingroup kernel_thread_tests
   * @brief Test abort on delayed thread before it has started
   * execution
   *
   * @see k_thread_abort()
   */
  void test_delayed_thread_abort(void)
  {
  	int current_prio = k_thread_priority_get(k_current_get());
  
  	execute_flag = 0;
  	/* Make current thread preemptive */
  	k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(2));
  
  	/* Create a preemptive thread of higher priority than
  	 * current thread
  	 */
  	k_tid_t tid = k_thread_create(&tdata, tstack, STACK_SIZE,
  				      (k_thread_entry_t)delayed_thread_entry, NULL, NULL, NULL,
  				      K_PRIO_PREEMPT(1), 0, K_MSEC(100));
  
  	/* Give up CPU */
  	k_msleep(50);
  
  	/* Test point: check if thread delayed for 100ms has not started*/
  	zassert_true(execute_flag == 0, "Delayed thread created is not"
  		     " put to wait queue");
  
  	k_thread_abort(tid);
  
  	/* Test point: Test abort of thread before its execution*/
  	zassert_false(execute_flag == 1, "Delayed thread is has executed"
  		      " before cancellation");
  
  	/* Restore the priority */
  	k_thread_priority_set(k_current_get(), current_prio);
  }
  
  static volatile bool isr_finished;
  
  static void offload_func(const void *param)
  {
  	struct k_thread *t = (struct k_thread *)param;
  
  	k_thread_abort(t);
  
  	/* k_thread_abort() in an isr shouldn't affect the ISR's execution */
  	isr_finished = true;
  }
  
  static void entry_abort_isr(void *p1, void *p2, void *p3)
  {
  	/* Simulate taking an interrupt which kills this thread */
  	irq_offload(offload_func, k_current_get());
  
  	printk("shouldn't see this, thread should have been killed");
  	ztest_test_fail();
  }
  
  extern struct k_sem offload_sem;
  
  /**
   * @ingroup kernel_thread_tests
   *
   * @brief Show that threads can be aborted from interrupt context by itself
   *
   * @details Spwan a thread, then enter ISR context in child thread and abort
   * the child thread. Check if ISR completed and target thread was aborted.
   *
   * @see k_thread_abort()
   */
  void test_abort_from_isr(void)
  {
  	isr_finished = false;
  	k_thread_create(&tdata, tstack, STACK_SIZE, entry_abort_isr,
  			NULL, NULL, NULL, 0, 0, K_NO_WAIT);
  
  
  	k_thread_join(&tdata, K_FOREVER);
  	zassert_true(isr_finished, "ISR did not complete");
  
  	/* Notice: Recover back the offload_sem: This is use for releasing
  	 * offload_sem which might be held when thread aborts itself in ISR
  	 * context, it will cause irq_offload cannot be used again.
  	 */
  	k_sem_give(&offload_sem);
  }
  
  /* use for sync thread start */
  static struct k_sem sem_abort;
  
  static void entry_aborted_thread(void *p1, void *p2, void *p3)
  {
  	k_sem_give(&sem_abort);
  
  	/* wait for being aborted */
  	while (1) {
  		k_sleep(K_MSEC(1));
  	}
  	zassert_unreachable("should not reach here");
  }
  
  /**
   * @ingroup kernel_thread_tests
   *
   * @brief Show that threads can be aborted from interrupt context
   *
   * @details Spwan a thread, then enter ISR context in main thread and abort
   * the child thread. Check if ISR completed and target thread was aborted.
   *
   * @see k_thread_abort()
   */
  void test_abort_from_isr_not_self(void)
  {
  	k_tid_t tid;
  
  	isr_finished = false;
  	k_sem_init(&sem_abort, 0, 1);
  
  	tid = k_thread_create(&tdata, tstack, STACK_SIZE, entry_aborted_thread,
  			NULL, NULL, NULL, 0, 0, K_NO_WAIT);
  
  	/* wait for thread started */
  	k_sem_take(&sem_abort, K_FOREVER);
  
  	/* Simulate taking an interrupt which kills spwan thread */
  	irq_offload(offload_func, (void *)tid);
  
  	k_thread_join(&tdata, K_FOREVER);
  	zassert_true(isr_finished, "ISR did not complete");
  }
  